From f413cb16e9832f3fdc2ec92e1ab37c168d334ef3 Mon Sep 17 00:00:00 2001 From: mbs Date: Sat, 9 Dec 2023 22:09:53 +0000 Subject: [PATCH] [bos#35161][EDF](2023-T1) Automatic backup - initial commit --- src/Model/Model_Document.cpp | 19 +- src/Model/Model_Document.h | 2 +- src/Model/Model_Session.cpp | 16 +- src/Model/Model_Session.h | 2 +- src/ModelAPI/ModelAPI_Session.h | 2 +- src/ModuleBase/ModuleBase_Preferences.cpp | 29 +++ src/SHAPERGUI/CMakeLists.txt | 3 + src/SHAPERGUI/SHAPERGUI.cpp | 300 +++++++++++++++++++++- src/SHAPERGUI/SHAPERGUI.h | 15 ++ src/SHAPERGUI/SHAPERGUI_CheckBackup.cpp | 127 +++++++++ src/SHAPERGUI/SHAPERGUI_CheckBackup.h | 45 ++++ src/SHAPERGUI/SHAPERGUI_DataModel.cpp | 37 ++- src/SHAPERGUI/SHAPERGUI_DataModel.h | 4 +- src/SHAPERGUI/SHAPERGUI_msg_fr.ts | 8 + src/XGUI/XGUI_Workshop.cpp | 64 ++++- src/XGUI/XGUI_Workshop.h | 8 + test.hdfs/testme.py | 2 +- 17 files changed, 661 insertions(+), 22 deletions(-) create mode 100644 src/SHAPERGUI/SHAPERGUI_CheckBackup.cpp create mode 100644 src/SHAPERGUI/SHAPERGUI_CheckBackup.h diff --git a/src/Model/Model_Document.cpp b/src/Model/Model_Document.cpp index 5f8b9fe53..df27cd5d1 100644 --- a/src/Model/Model_Document.cpp +++ b/src/Model/Model_Document.cpp @@ -87,6 +87,15 @@ # define _separator_ '/' #endif +//--------------------------------------------------------- +#define USE_DEBUG +#define MB_IGNORE_QT +//#define MB_FULL_DUMP +#define MBCLASSNAME "Model_Document" +#include "MBDebug.h" +// <-- insert includes for addtional debug headers here! +//--------------------------------------------------------- + static const int UNDO_LIMIT = 1000; // number of possible undo operations (big for sketcher) static const int TAG_GENERAL = 1; // general properties tag @@ -448,6 +457,7 @@ static bool saveDocument(Handle(Model_Application) theApp, Handle(TDocStd_Document) theDoc, const TCollection_ExtendedString& theFilename) { + DBG_FUNC(); PCDM_StoreStatus aStatus; try { // create the directory to save the document @@ -485,8 +495,15 @@ static bool saveDocument(Handle(Model_Application) theApp, } bool Model_Document::save( - const char* theDirName, const char* theFileName, std::list& theResults) + const char* theDirName, const char* theFileName, + std::list& theResults, + bool doBackup/*=false*/) { + DBG_FUN(); + ARG(theDirName); + ARG(theFileName); + ARG(doBackup); + // if the history line is not in the end, move it to the end before save, otherwise // problems with results restore and (the most important) naming problems will appear // due to change evolution to SELECTION (problems in NamedShape and Name) diff --git a/src/Model/Model_Document.h b/src/Model/Model_Document.h index 1dec08f14..0e8670e85 100644 --- a/src/Model/Model_Document.h +++ b/src/Model/Model_Document.h @@ -73,7 +73,7 @@ class Model_Document : public ModelAPI_Document //! \param theResults the result full file names that were stored by "save" //! \returns true if file was stored successfully MODEL_EXPORT virtual bool save( - const char* theDirName, const char* theFileName, std::list& theResults); + const char* theDirName, const char* theFileName, std::list& theResults, bool doBackup=false); //! Export the list of features to the file //! \param theFilename path to save the file diff --git a/src/Model/Model_Session.cpp b/src/Model/Model_Session.cpp index b80cdd8ea..bc89ab43a 100644 --- a/src/Model/Model_Session.cpp +++ b/src/Model/Model_Session.cpp @@ -52,6 +52,15 @@ #include +//--------------------------------------------------------- +#define USE_DEBUG +#define MB_IGNORE_QT +//#define MB_FULL_DUMP +#define MBCLASSNAME "Model_Session" +#include "MBDebug.h" +// <-- insert includes for addtional debug headers here! +//--------------------------------------------------------- + static Model_Session* myImpl = new Model_Session(); // to redirect all calls to the root document @@ -65,9 +74,12 @@ bool Model_Session::load(const char* theFileName) return aRes; } -bool Model_Session::save(const char* theFileName, std::list& theResults) +bool Model_Session::save(const char* theFileName, std::list& theResults, bool doBackup=false) { - return ROOT_DOC->save(theFileName, "root", theResults); + DBG_FUN(); + ARG(theFileName); + ARG(doBackup); + return ROOT_DOC->save(theFileName, "root", theResults, doBackup); } void Model_Session::closeAll() diff --git a/src/Model/Model_Session.h b/src/Model/Model_Session.h index c74f95286..5a5db8865 100644 --- a/src/Model/Model_Session.h +++ b/src/Model/Model_Session.h @@ -66,7 +66,7 @@ class Model_Session : public ModelAPI_Session, public Events_Listener //! \param theFileName full name of the file to store //! \param theResults the result full file names that were stored by "save" //! \returns true if file was stored successfully - MODEL_EXPORT virtual bool save(const char* theFileName, std::list& theResults); + MODEL_EXPORT virtual bool save(const char* theFileName, std::list& theResults, bool dobackup=false); //! Closes all documents MODEL_EXPORT virtual void closeAll(); diff --git a/src/ModelAPI/ModelAPI_Session.h b/src/ModelAPI/ModelAPI_Session.h index 566158c60..732884eff 100644 --- a/src/ModelAPI/ModelAPI_Session.h +++ b/src/ModelAPI/ModelAPI_Session.h @@ -59,7 +59,7 @@ protected: //! \param theFileName full name of the file to store //! \param theResults the result full file names that were stored by "save" //! \returns true if file was stored successfully - virtual bool save(const char* theFileName, std::list& theResults) = 0; + virtual bool save(const char* theFileName, std::list& theResults, bool isBackup=false) = 0; //! Closes all documents virtual void closeAll() = 0; diff --git a/src/ModuleBase/ModuleBase_Preferences.cpp b/src/ModuleBase/ModuleBase_Preferences.cpp index e7827fbad..38aaf1fc6 100644 --- a/src/ModuleBase/ModuleBase_Preferences.cpp +++ b/src/ModuleBase/ModuleBase_Preferences.cpp @@ -220,6 +220,35 @@ void ModuleBase_Preferences::createGeneralTab(ModuleBase_IPrefMgr* thePref, int "part_visualization_study"); thePref->setItemProperty("strings", visuItemList, visuId); thePref->setItemProperty("indexes", visuIdList, visuId); + +#if 1 + // Group related to automatic backup + group = thePref->addPreference(QObject::tr("Automatically backup a study"), generalTab, + SUIT_PreferenceMgr::Auto, QString(), QString()); + + thePref->addPreference(QObject::tr("Enable automatic backup"), group, SUIT_PreferenceMgr::Bool, + ModuleBase_Preferences::GENERAL_SECTION, "use_auto_backup"); + + int delay = thePref->addPreference(QObject::tr("Backup Interval" ), group, SUIT_PreferenceMgr::IntSpin, + ModuleBase_Preferences::GENERAL_SECTION, "backup_interval" ); + thePref->setItemProperty( "min", 5, delay ); + thePref->setItemProperty( "max", 1440, delay ); + thePref->setItemProperty( "suffix", " min", delay ); + + int folderPref = thePref->addPreference(QObject::tr("Backup Folder"), group, SUIT_PreferenceMgr::File, "backup_folder", QString()/*platform*/); + thePref->setItemProperty( "mode", Qtx::PT_Directory, folderPref ); + + visuItemList.clear(); + visuItemList << QObject::tr("Store last backup only") + << QObject::tr("Store full backup history"); + visuIdList.clear(); + visuIdList << 0 << 1; + visuId = thePref->addPreference(QObject::tr("Backup Storage"), group, SUIT_PreferenceMgr::Selector, + ModuleBase_Preferences::GENERAL_SECTION, + "auto_backup_storage"); + thePref->setItemProperty("strings", visuItemList, visuId); + thePref->setItemProperty("indexes", visuIdList, visuId); +#endif } void ModuleBase_Preferences::updateSketchTab(ModuleBase_IPrefMgr* thePref, int thePageId) diff --git a/src/SHAPERGUI/CMakeLists.txt b/src/SHAPERGUI/CMakeLists.txt index efc731ca2..2b1886b1b 100644 --- a/src/SHAPERGUI/CMakeLists.txt +++ b/src/SHAPERGUI/CMakeLists.txt @@ -30,6 +30,7 @@ SET(UPDATE_TRANSLATION OFF) SET(PROJECT_HEADERS SHAPER_SHAPERGUI.h SHAPERGUI.h + SHAPERGUI_CheckBackup.h SHAPERGUI_DataModel.h SHAPERGUI_OCCSelector.h SHAPERGUI_SalomeViewer.h @@ -39,6 +40,7 @@ SET(PROJECT_HEADERS SET(PROJECT_MOC_HEADERS SHAPERGUI.h + SHAPERGUI_CheckBackup.h SHAPERGUI_DataModel.h SHAPERGUI_NestedButton.h SHAPERGUI_SalomeViewer.h @@ -50,6 +52,7 @@ QT_WRAP_MOC(PROJECT_AUTOMOC ${PROJECT_MOC_HEADERS}) SET(PROJECT_SOURCES SHAPERGUI.cpp + SHAPERGUI_CheckBackup.cpp SHAPERGUI_DataModel.cpp SHAPERGUI_OCCSelector.cpp SHAPERGUI_SalomeViewer.cpp diff --git a/src/SHAPERGUI/SHAPERGUI.cpp b/src/SHAPERGUI/SHAPERGUI.cpp index b18573838..4b61fa21d 100644 --- a/src/SHAPERGUI/SHAPERGUI.cpp +++ b/src/SHAPERGUI/SHAPERGUI.cpp @@ -18,6 +18,7 @@ // #include "SHAPERGUI.h" +#include "SHAPERGUI_CheckBackup.h" #include "SHAPERGUI_DataModel.h" #include "SHAPERGUI_OCCSelector.h" #include "SHAPERGUI_NestedButton.h" @@ -76,10 +77,25 @@ #include #include #include +#include +#include + +#include +#include +#include #include #include +//--------------------------------------------------------- +#define USE_DEBUG +//#define MB_IGNORE_QT +//#define MB_FULL_DUMP +#define MBCLASSNAME "SHAPERGUI" +#include "MBDebug.h" +// <-- insert includes for addtional debug headers here! +//--------------------------------------------------------- + #if OCC_VERSION_HEX < 0x070400 #define SALOME_PATCH_FOR_CTRL_WHEEL #endif @@ -136,6 +152,13 @@ private: }; +// static void DumpOperations(XGUI_Workshop *wshop) +// { +// XGUI_OperationMgr *aOpMgr = wshop->operationMgr(); +// if (!aOpMgr) return; +// QStringList operations = aOpMgr->operationList(); +// SHOW(operations); +// } //****************************************************** @@ -145,6 +168,7 @@ SHAPERGUI::SHAPERGUI() myInspectionPanel(0), myIsFacesPanelVisible(false), myIsToolbarsModified(false), myAxisArrowRate(-1) { + DBG_FUN(); myWorkshop = new XGUI_Workshop(this); connect(myWorkshop, SIGNAL(commandStatusUpdated()), this, SLOT(onUpdateCommandStatus())); @@ -153,6 +177,12 @@ SHAPERGUI::SHAPERGUI() ModuleBase_Preferences::setResourceMgr(application()->resourceMgr()); + // initialize backup timer + myBackupTimer = new QTimer( this ); + myBackupTimer->setSingleShot( true ); + connect( myBackupTimer, SIGNAL( timeout() ), this, SLOT( onBackupDoc() ) ); + connect( this, SIGNAL( backupDone(QString,bool) ), this, SLOT( onBackupDone(QString,bool) )); + // It will be called in XGUI_Workshop::startApplication // ModuleBase_Preferences::loadCustomProps(); } @@ -160,6 +190,7 @@ SHAPERGUI::SHAPERGUI() //****************************************************** SHAPERGUI::~SHAPERGUI() { + DBG_FUN(); delete myWorkshop; delete myProxyViewer; } @@ -167,6 +198,7 @@ SHAPERGUI::~SHAPERGUI() //****************************************************** void SHAPERGUI::initialize(CAM_Application* theApp) { + DBG_FUN(); LightApp_Module::initialize(theApp); myWorkshop->startApplication(); @@ -281,6 +313,7 @@ void SHAPERGUI::viewManagers(QStringList& theList) const //****************************************************** bool SHAPERGUI::activateModule(SUIT_Study* theStudy) { + DBG_FUN(); ModelAPI_Session::get()->moduleDocument(); // initialize a root document if not done yet // this must be done in the initialization and in activation (on the second activation @@ -383,6 +416,22 @@ bool SHAPERGUI::activateModule(SUIT_Study* theStudy) this, SLOT(onSaveDocByShaper())); connect(getApp()->action(LightApp_Application::FileSaveAsId), SIGNAL(triggered(bool)), this, SLOT(onSaveAsDocByShaper())); + + //MBS: + SUIT_ResourceMgr* aResMgr = application()->resourceMgr(); + if ( aResMgr && application()->activeStudy() ) { + bool useBackup = aResMgr->booleanValue( "General", "use_auto_backup", true ); + if (useBackup) { + int backupInterval = aResMgr->integerValue( "General", "backup_interval", 1 ); + if ( backupInterval > 0 ){ + backupInterval = 1; + MSGEL("....starting BackupTimer: interval=" << backupInterval); + myBackupTimer->start( backupInterval*60000 ); + } + } + } + //connect(); + updateInfoPanel(); //connect(myWorkshop->operationMgr(), SIGNAL(operationResumed(ModuleBase_Operation*)), @@ -400,6 +449,7 @@ bool SHAPERGUI::activateModule(SUIT_Study* theStudy) //****************************************************** void SHAPERGUI::hideInternalWindows() { + DBG_FUN(); myProxyViewer->activateViewer(false); setMenuShown(false); setToolShown(false); @@ -428,14 +478,17 @@ void SHAPERGUI::hideInternalWindows() //****************************************************** bool SHAPERGUI::deactivateModule(SUIT_Study* theStudy) { + DBG_FUN(); saveToolbarsConfig(); myWorkshop->deactivateModule(); + MSGEL("....stopping BackupTimer"); + myBackupTimer->stop(); + myIsInspectionVisible = myInspectionPanel->isVisible(); myIsFacesPanelVisible = myWorkshop->facesPanel()->isVisible(); hideInternalWindows(); - // the active operation should be stopped for the next activation. // There should not be active operation and visualized preview. // Abort operation should be performed before the selection's remove @@ -530,21 +583,77 @@ static void onOperationGeneric( ModuleBase_Operation* theOperation, event ); } +class WaitBackupResetter { +public: + WaitBackupResetter(XGUI_Workshop *theWshop) + :myWorkshop(theWshop) + { + } + WaitBackupResetter() = delete; + ~WaitBackupResetter() + { + if (myWorkshop) + { + myWorkshop->setWaitForBackup(false); + myWorkshop = nullptr; + } + } +private: + XGUI_Workshop *myWorkshop; +}; + //****************************************************** void SHAPERGUI::onOperationCommitted(ModuleBase_Operation* theOperation) { + DBG_FUN(); + SHOW(theOperation->id()); + onOperationGeneric(theOperation, moduleName(), "committed"); + + if (myWorkshop && myWorkshop->waitForBackup()) + { + XGUI_OperationMgr *operMgr = myWorkshop->operationMgr(); + if (operMgr && operMgr->hasOperation()) + { + // If the user is still running an operation, e.g. we left the line creation + // during sketch creation, do not yet create the backup + std::cout << "---> There are still active operations!" << std::endl; + return; + } + // There are no more active operations => we can now do the backup + WaitBackupResetter _resetter(myWorkshop); + onBackupDoc(); + } } //****************************************************** void SHAPERGUI::onOperationAborted(ModuleBase_Operation* theOperation) { + DBG_FUN(); + SHOW(theOperation->id()); + onOperationGeneric(theOperation, moduleName(), "aborted"); + + if (myWorkshop && myWorkshop->waitForBackup()) + { + XGUI_OperationMgr *operMgr = myWorkshop->operationMgr(); + if (operMgr && operMgr->hasOperation()) + { + // If the user is still running an operation, e.g. we left the line creation + // during sketch creation, do not yet create the backup + std::cout << "---> There are still active operations!" << std::endl; + return; + } + // There are no more active operations => we can now do the backup + WaitBackupResetter _resetter(myWorkshop); + onBackupDoc(); + } } //****************************************************** void SHAPERGUI::onViewManagerAdded(SUIT_ViewManager* theMgr) { + DBG_FUN(); if (!mySelector) { mySelector = createSelector(theMgr); myWorkshop->selectionActivate()->updateSelectionFilters(); @@ -565,6 +674,7 @@ void SHAPERGUI::onViewManagerAdded(SUIT_ViewManager* theMgr) //****************************************************** void SHAPERGUI::onViewManagerRemoved(SUIT_ViewManager* theMgr) { + DBG_FUN(); if (mySelector) { if (theMgr->getType() == OCCViewer_Viewer::Type()) { OCCViewer_Viewer* aViewer = static_cast(theMgr->getViewModel()); @@ -603,6 +713,7 @@ QtxPopupMgr* SHAPERGUI::popupMgr() //****************************************************** void SHAPERGUI::onDefaultPreferences() { + DBG_FUN(); // reset main resources ModuleBase_Preferences::resetResourcePreferences(preferences()); // reset plugin's resources @@ -614,6 +725,7 @@ void SHAPERGUI::onDefaultPreferences() //****************************************************** void SHAPERGUI::onScriptLoaded() { + DBG_FUN(); // this slot is called after processing of the LoadScriptId action of SalomeApp Application // Each dumped script contains updateObjBrowser() that creates a new instance of Object // Browser. When SHAPER module is active, this browser should not be used. It might be removed @@ -629,6 +741,7 @@ void SHAPERGUI::onScriptLoaded() //****************************************************** void SHAPERGUI::onSaveDocByShaper() { + DBG_FUN(); if(!workshop()->operationMgr()->abortAllOperations(XGUI_OperationMgr::XGUI_InformationMessage)) return; @@ -638,12 +751,153 @@ void SHAPERGUI::onSaveDocByShaper() //****************************************************** void SHAPERGUI::onSaveAsDocByShaper() { + DBG_FUN(); if(!workshop()->operationMgr()->abortAllOperations(XGUI_OperationMgr::XGUI_InformationMessage)) return; getApp()->onSaveAsDoc(); } +//****************************************************** +void SHAPERGUI::onBackupDoc() +{ + DBG_FUN(); + MSGEL(" ...backing up current study"); + + if (myWorkshop && myWorkshop->operationMgr()) + { + if (myWorkshop->operationMgr()->hasOperation()) + { + // If the user is running an operation, only do the backup + // after the operation has finished (committed or cancelled). + myWorkshop->setWaitForBackup(); + return; + } + // Run the backup in a separate thread!! + myBackupResult = std::async(std::launch::async, [this]{return this->backupDoc();}); + } +} + +class LockBackupState { +public: + LockBackupState(XGUI_Workshop *wshop) : myWorkshop(wshop) + { + if (myWorkshop) myWorkshop->setBackupState(); + } + ~LockBackupState() + { + if (myWorkshop) myWorkshop->setBackupState(false); + myWorkshop = nullptr; + } + XGUI_Workshop *myWorkshop; +}; + +//****************************************************** +bool SHAPERGUI::backupDoc() +{ + DBG_FUN(); + + bool isOk = false; +#if 0 + MSGEL("...sleep for 5 seconds"); + std::this_thread::sleep_for(std::chrono::seconds(5)); + isOk = true; + QString aName("/some/file/name.hdf"); +#else + SUIT_Study* study = application()->activeStudy(); + if ( !study ) + return false; + + // if ( !abortAllOperations() ) + // return false; + + LockBackupState lockBackup(myWorkshop); + + QString aName = study->studyName(); + try + { + SHOW(aName); + if ( aName.isNull() ) + return false; + + const QChar aSep = QDir::separator(); + + QString aFolder("/home/bernhard/backups"); + + QDateTime now = QDateTime::currentDateTime(); + aFolder += aSep + now.toString("yyyyMMdd_hhmmss"); + + QDir aDir(aFolder); + if (!aDir.exists()) { + aDir.mkpath(aFolder); + aDir.mkdir(aFolder); + } + + if (study->isSaved()) { + // Retrieve the filename only from the fullpath + QFileInfo fi(aName); + aName = fi.completeBaseName(); + } + QString aFullName = aFolder + aSep + aName + QString(".hdf"); + SHOW(aFullName); + + // Save the study into a single HDF file + isOk = study->saveDocumentAs( aFullName, true ); + if (!isOk){ + MSGEL("ERR: failed to backup study document"); + return isOk; + } + + // Now, dump the python script + LightApp_Study *lightStudy = dynamic_cast(study); + if (!lightStudy) + return false; + + aFullName = aFolder + aSep + aName + QString(".py"); + isOk = lightStudy->dump(aFullName, true, false, false); + if (!isOk){ + MSGEL("ERR: failed to backup python script"); + return isOk; + } + + // Finally start another salome process and reload the saved document & script for verification + SHAPERGUI_CheckBackup checkBackup(aFolder, aName); + QString testScript("/home/bernhard/backups/checkValidity.py"); + int aResult = checkBackup.run(testScript); + isOk = (aResult == 0); + } + catch (std::exception &ex) + { + std::cout << "ERROR: std::exception caught" << std::endl; + isOk = false; + } + catch (...) + { + std::cout << "ERROR: unknown exception caught" << std::endl; + isOk = false; + } + #endif + + MSGEL("...emit backupDone signal"); + emit backupDone(aName, isOk); + return isOk; +} + +//****************************************************** +void SHAPERGUI::onBackupDone(QString aName, bool aResult) +{ + DBG_FUN(); + ARG(aName); + ARG(aResult); + + bool isOk = myBackupResult.get(); + SHOW(isOk); + QFileInfo fi(aName); + aName = fi.canonicalPath(); + SHOW(aName); + putInfo( isOk ? tr("Backup done in folder: %1").arg(aName) : tr("Failed to backup active study!"), 5000 ); +} + //****************************************************** void SHAPERGUI::onUpdateCommandStatus() { @@ -658,6 +912,7 @@ void SHAPERGUI::onUpdateCommandStatus() //****************************************************** SHAPERGUI_OCCSelector* SHAPERGUI::createSelector(SUIT_ViewManager* theMgr) { + DBG_FUN(); if (theMgr->getType() == OCCViewer_Viewer::Type()) { OCCViewer_Viewer* aViewer = static_cast(theMgr->getViewModel()); @@ -697,6 +952,7 @@ SHAPERGUI_OCCSelector* SHAPERGUI::createSelector(SUIT_ViewManager* theMgr) //****************************************************** CAM_DataModel* SHAPERGUI::createDataModel() { + DBG_FUN(); return new SHAPERGUI_DataModel(this); } @@ -924,6 +1180,7 @@ void SHAPERGUI::contextMenuPopup(const QString& theClient, QMenu* theMenu, QStri //****************************************************** void SHAPERGUI::createPreferences() { + DBG_FUN(); LightApp_Preferences* aPref = preferences(); if (!aPref) return; @@ -1017,6 +1274,9 @@ void SHAPERGUI::createPreferences() //****************************************************** void SHAPERGUI::preferencesChanged(const QString& theSection, const QString& theParam) { + DBG_FUN(); + ARG(theSection); + ARG(theParam); SUIT_ResourceMgr* aResMgr = application()->resourceMgr(); QString aVal = aResMgr->stringValue(theSection, theParam); Config_Prop* aProp = Config_PropManager::findProp(theSection.toStdString(), @@ -1081,11 +1341,33 @@ void SHAPERGUI::preferencesChanged(const QString& theSection, const QString& the } } } - else if (theSection == ModuleBase_Preferences::GENERAL_SECTION && theParam == "create_init_part") { - bool aCreate = ModuleBase_Preferences::resourceMgr()->booleanValue( - ModuleBase_Preferences::GENERAL_SECTION, "create_init_part", true); - Events_MessageBool aCreateMsg(Events_Loop::eventByName(EVENT_CREATE_PART_ON_START), aCreate); - aCreateMsg.send(); + else if (theSection == ModuleBase_Preferences::GENERAL_SECTION) { + if (theParam == "create_init_part") { + bool aCreate = ModuleBase_Preferences::resourceMgr()->booleanValue( + ModuleBase_Preferences::GENERAL_SECTION, "create_init_part", true); + Events_MessageBool aCreateMsg(Events_Loop::eventByName(EVENT_CREATE_PART_ON_START), aCreate); + aCreateMsg.send(); + } + else if (theParam == "use_auto_backup") { + bool useBackup = ModuleBase_Preferences::resourceMgr()->booleanValue( + ModuleBase_Preferences::GENERAL_SECTION, "use_auto_backup", true); + if (useBackup) { + int backupInterval = aResMgr->integerValue( ModuleBase_Preferences::GENERAL_SECTION, "backup_interval", 1 ); + if ( backupInterval > 0 ){ + backupInterval = 1; + MSGEL("....starting BackupTimer: interval=" << backupInterval << " min"); + myBackupTimer->start( backupInterval*60000 ); + } + else { + MSGEL("....stopping backup timer"); + myBackupTimer->stop(); + } + } + else { + MSGEL("....stopping backup timer"); + myBackupTimer->stop(); + } + } } else if (theSection == ModuleBase_Preferences::VIEWER_SECTION && theParam.startsWith("group_names_")) @@ -1103,11 +1385,13 @@ void SHAPERGUI::putInfo(const QString& theInfo, const int theMSecs) bool SHAPERGUI::abortAllOperations() { + DBG_FUN(); return workshop()->operationMgr()->abortAllOperations(); } void SHAPERGUI::createFeatureActions() { + DBG_FUN(); myWorkshop->menuMgr()->createFeatureActions(); } @@ -1237,6 +1521,7 @@ void SHAPERGUI::updateToolbars(const QMap& theNewToolbars) void SHAPERGUI::saveToolbarsConfig() { + DBG_FUN(); if (!myIsToolbarsModified) return; // Save toolbars configuration into map @@ -1285,6 +1570,7 @@ void SHAPERGUI::saveToolbarsConfig() void SHAPERGUI::loadToolbarsConfig() { + DBG_FUN(); SUIT_ResourceMgr* aResMgr = application()->resourceMgr(); QStringList aToolbarNames = aResMgr->parameters(ToolbarsSection); if (aToolbarNames.size() == 0) @@ -1385,6 +1671,7 @@ QIntList SHAPERGUI::getFreeCommands() const void SHAPERGUI::resetToolbars() { + DBG_FUN(); if (!myDefaultToolbars.isEmpty()) updateToolbars(myDefaultToolbars); myIsToolbarsModified = false; @@ -1394,6 +1681,7 @@ void SHAPERGUI::resetToolbars() void SHAPERGUI::publishToStudy() { + DBG_FUN(); if (isActiveModule() && ModelAPI_Session::get()->hasModuleDocument()) { myWorkshop->module()->launchOperation("PublishToStudy", false); diff --git a/src/SHAPERGUI/SHAPERGUI.h b/src/SHAPERGUI/SHAPERGUI.h index 2a84accc3..173426f82 100644 --- a/src/SHAPERGUI/SHAPERGUI.h +++ b/src/SHAPERGUI/SHAPERGUI.h @@ -30,6 +30,7 @@ #include #include +#include class XGUI_Workshop; class SHAPERGUI_OCCSelector; @@ -177,6 +178,9 @@ Q_OBJECT virtual void updateInfoPanel(); + signals: + void backupDone(QString aName, bool aResult); + public slots: /// \brief The method is redefined to connect to the study viewer before the data /// model is filled by opened file. This file open will flush redisplay signals for, @@ -212,6 +216,12 @@ Q_OBJECT /// Save application functionality with additional processing of aborting the current operation void onSaveAsDocByShaper(); + /// Backup document functionality with additional file validation + void onBackupDoc(); + + /// Document has been backed-up + void onBackupDone(QString aName, bool aResult); + /// Obtains the current application and updates its actions void onUpdateCommandStatus(); @@ -228,6 +238,8 @@ Q_OBJECT /// Abort all operations virtual bool abortAllOperations(); + bool backupDoc(); + private slots: void onWhatIs(bool isToggled); @@ -306,6 +318,9 @@ private slots: Handle(Graphic3d_AspectMarker3d) myHighlightPointAspect; double myAxisArrowRate; + + QTimer* myBackupTimer; + std::future myBackupResult; }; #endif diff --git a/src/SHAPERGUI/SHAPERGUI_CheckBackup.cpp b/src/SHAPERGUI/SHAPERGUI_CheckBackup.cpp new file mode 100644 index 000000000..b5e643e49 --- /dev/null +++ b/src/SHAPERGUI/SHAPERGUI_CheckBackup.cpp @@ -0,0 +1,127 @@ +// Copyright (C) 2023 CEA, EDF, OPEN CASCADE SAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#include "SHAPERGUI_CheckBackup.h" +#include +#include +#include + +//--------------------------------------------------------- +#define USE_DEBUG +//#define MB_IGNORE_QT +//#define MB_FULL_DUMP +#define MBCLASSNAME "SHAPERGUI_CheckBackup" +#include "MBDebug.h" +// <-- insert includes for addtional debug headers here! +//--------------------------------------------------------- + + + +SHAPERGUI_CheckBackup::SHAPERGUI_CheckBackup(const QString &theFolder, const QString &theBaseName) + : myProcess(nullptr) + , myFolder(theFolder) + , myBaseName(theBaseName) +{ + DBG_FUN(); + ARG(theFolder); + ARG(theBaseName); +} + + +int SHAPERGUI_CheckBackup::run(const QString &theTestScript) +{ + DBG_FUN(); + ARG(theTestScript); + + int aResult = 0; + if (myProcess) + return 1; + + QString aProgName = std::getenv("PYTHONBIN"); + if (aProgName.isEmpty()) + aProgName = "python3"; + + const QChar aSep = QDir::separator(); + + QString testBackup("test_backup.py"); + QStringList dirs; +#if 0 + dirs << QString(std::getenv("SHAPER_ROOT_DIR")) + << QString("bin") + << QString("salome") + << testBackup; +#else + dirs << QString(std::getenv("HOME")) + << QString("S2") + << QString("SALOME-MBS-DB11") + << testBackup; +#endif + QString scriptName = dirs.join( QDir::separator() ); + + QStringList args; + args << scriptName << myFolder << myBaseName << theTestScript; + + myProcess = new QProcess(this); + if (!myProcess) + return 2; + + connect(myProcess, SIGNAL(started()), this, SLOT(procStarted())); + connect(myProcess, SIGNAL(error(QProcess::ProcessError)), this, + SLOT(procError(QProcess::ProcessError))); + connect(myProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, + SLOT(procFinished(int, QProcess::ExitStatus))); + + SHOW(aProgName); + SHOW(args); + myProcess->setStandardOutputFile((QStringList() << myFolder << myBaseName+".log").join(aSep)); + myProcess->start(aProgName, args); + myProcess->waitForFinished(300000); + int exitStat = myProcess->exitStatus(); + SHOW(exitStat); + int exitCode = myProcess->exitCode(); + SHOW(exitCode); + if (exitStat == 0/*NormalExit*/ && exitCode != 0) + aResult = exitCode; + else if (exitStat == -1/*CrashExit*/) + aResult = 99; + myProcess->deleteLater(); + + return 0; +} + + +void SHAPERGUI_CheckBackup::procStarted() +{ + DBG_FUN(); +} + + +void SHAPERGUI_CheckBackup::procFinished(int code, QProcess::ExitStatus stat) +{ + DBG_FUN(); + ARG(code); + ARG2(stat, int); +} + + +void SHAPERGUI_CheckBackup::procError(QProcess::ProcessError err) +{ + DBG_FUN(); + ARG2(err, int); +} diff --git a/src/SHAPERGUI/SHAPERGUI_CheckBackup.h b/src/SHAPERGUI/SHAPERGUI_CheckBackup.h new file mode 100644 index 000000000..b491912f0 --- /dev/null +++ b/src/SHAPERGUI/SHAPERGUI_CheckBackup.h @@ -0,0 +1,45 @@ +// Copyright (C) 2023 CEA, EDF, OPEN CASCADE SAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#ifndef SHAPERGUI_CHECKBACKUP_H +#define SHAPERGUI_CHECKBACKUP_H + +#include "SHAPER_SHAPERGUI.h" +#include + + +class SHAPERGUI_EXPORT SHAPERGUI_CheckBackup : public QObject +{ + Q_OBJECT +public: + SHAPERGUI_CheckBackup(const QString &theFolder, const QString &theBaseName); + int run(const QString &theTestScript); + +private slots: + void procStarted(); + void procFinished(int, QProcess::ExitStatus); + void procError(QProcess::ProcessError); + +private: + QProcess* myProcess; + QString myFolder; + QString myBaseName; +}; + +#endif diff --git a/src/SHAPERGUI/SHAPERGUI_DataModel.cpp b/src/SHAPERGUI/SHAPERGUI_DataModel.cpp index a8856b26d..1769826a2 100644 --- a/src/SHAPERGUI/SHAPERGUI_DataModel.cpp +++ b/src/SHAPERGUI/SHAPERGUI_DataModel.cpp @@ -37,20 +37,34 @@ #include #include +//--------------------------------------------------------- +#define USE_DEBUG +//#define MB_IGNORE_QT +//#define MB_FULL_DUMP +#define MBCLASSNAME "SHAPERGUI_DataModel" +#include "MBDebug.h" +// <-- insert includes for addtional debug headers here! +//--------------------------------------------------------- + #define DUMP_NAME "shaper_dump.py" SHAPERGUI_DataModel::SHAPERGUI_DataModel(SHAPERGUI* theModule) : LightApp_DataModel(theModule), myStudyPath(""), myModule(theModule) { + DBG_FUN(); } SHAPERGUI_DataModel::~SHAPERGUI_DataModel() { + DBG_FUN(); } bool SHAPERGUI_DataModel::open(const QString& thePath, CAM_Study* theStudy, QStringList theFiles) { + DBG_FUN(); + ARG(thePath); + ARG(theFiles); LightApp_DataModel::open( thePath, theStudy, theFiles ); if (theFiles.size() == 0) return false; @@ -83,12 +97,17 @@ bool SHAPERGUI_DataModel::open(const QString& thePath, CAM_Study* theStudy, QStr return true; } -bool SHAPERGUI_DataModel::save(QStringList& theFiles) +bool SHAPERGUI_DataModel::save(QStringList& theFiles, bool isBackup/*=false*/) { + DBG_FUN(); + ARG(theFiles); + ARG(isBackup); // Publish to study before saving of the data model - myModule->publishToStudy(); + //if (!isBackup) + myModule->publishToStudy(); - LightApp_DataModel::save( theFiles ); + //if (!isBackup) + LightApp_DataModel::save( theFiles ); XGUI_Workshop* aWorkShop = myModule->workshop(); std::list aFileNames; @@ -120,14 +139,19 @@ bool SHAPERGUI_DataModel::save(QStringList& theFiles) return true; } -bool SHAPERGUI_DataModel::saveAs(const QString& thePath, CAM_Study* theStudy, QStringList& theFiles) +bool SHAPERGUI_DataModel::saveAs(const QString& thePath, CAM_Study* theStudy, QStringList& theFiles, bool isBackup/*=false*/) { + DBG_FUN(); + ARG(thePath); + ARG(theFiles); + ARG(isBackup); myStudyPath = thePath; - return save(theFiles); + return save(theFiles, isBackup); } bool SHAPERGUI_DataModel::close() { + DBG_FUN(); myModule->workshop()->closeDocument(); return LightApp_DataModel::close(); } @@ -174,6 +198,9 @@ void SHAPERGUI_DataModel::removeDirectory(const QString& theDirectoryName) bool SHAPERGUI_DataModel::dumpPython(const QString& thePath, CAM_Study* theStudy, bool isMultiFile, QStringList& theListOfFiles) { + DBG_FUN(); + ARG(thePath); + ARG(theListOfFiles); LightApp_Study* aStudy = dynamic_cast(theStudy); if (!aStudy) return false; diff --git a/src/SHAPERGUI/SHAPERGUI_DataModel.h b/src/SHAPERGUI/SHAPERGUI_DataModel.h index 571d1b76a..3cba44fcd 100644 --- a/src/SHAPERGUI/SHAPERGUI_DataModel.h +++ b/src/SHAPERGUI/SHAPERGUI_DataModel.h @@ -46,13 +46,13 @@ class SHAPERGUI_EXPORT SHAPERGUI_DataModel : public LightApp_DataModel /// Save module data to file /// \param theFiles list of created files - virtual bool save(QStringList& theFiles); + virtual bool save(QStringList& theFiles, bool isBackup=false); /// Save module data to a file /// \param thePath a path to the directory /// \param theStudy a current study /// \param theFiles a list of files to open - virtual bool saveAs(const QString& thePath, CAM_Study* theStudy, QStringList& theFiles); + virtual bool saveAs(const QString& thePath, CAM_Study* theStudy, QStringList& theFiles, bool isBackup=false); /// Close data structure virtual bool close(); diff --git a/src/SHAPERGUI/SHAPERGUI_msg_fr.ts b/src/SHAPERGUI/SHAPERGUI_msg_fr.ts index 740d35f89..2bf82b282 100644 --- a/src/SHAPERGUI/SHAPERGUI_msg_fr.ts +++ b/src/SHAPERGUI/SHAPERGUI_msg_fr.ts @@ -182,6 +182,14 @@ Text color Couleur du texte + + Backup done in folder: %1 + Sauvegarde effectuée dans le dossier: %1 + + + Failed to backup active study! + Échec de la sauvegarde de l'étude active! + SHAPERGUI_ToolbarItemsDlg diff --git a/src/XGUI/XGUI_Workshop.cpp b/src/XGUI/XGUI_Workshop.cpp index 6ae78b95d..905eb509f 100644 --- a/src/XGUI/XGUI_Workshop.cpp +++ b/src/XGUI/XGUI_Workshop.cpp @@ -180,6 +180,15 @@ static Handle(VInspector_CallBack) MyVCallBack; #include #endif +//--------------------------------------------------------- +#define USE_DEBUG +//#define MB_IGNORE_QT +//#define MB_FULL_DUMP +#define MBCLASSNAME "XGUI_Workshop" +#include "MBDebug.h" +// <-- insert includes for addtional debug headers here! +//--------------------------------------------------------- + //#define DEBUG_WITH_MESSAGE_REPORT QString XGUI_Workshop::MOVE_TO_END_COMMAND = QObject::tr("Move to the end"); @@ -212,8 +221,10 @@ XGUI_Workshop::XGUI_Workshop(XGUI_SalomeConnector* theConnector) myDisplayer(0), mySalomeConnector(theConnector), //myViewerSelMode(TopAbs_FACE), - myInspectionPanel(0) + myInspectionPanel(0), + myDoBackup(false) { + DBG_FUN(); mySelector = new XGUI_SelectionMgr(this); myModuleConnector = new XGUI_ModuleConnector(this); myOperationMgr = new XGUI_OperationMgr(this, myModuleConnector); @@ -315,6 +326,7 @@ XGUI_Workshop::XGUI_Workshop(XGUI_SalomeConnector* theConnector) //****************************************************** XGUI_Workshop::~XGUI_Workshop(void) { + DBG_FUN(); #ifdef _DEBUG #ifdef MISSED_TRANSLATION // Save Missed translations @@ -332,6 +344,7 @@ XGUI_Workshop::~XGUI_Workshop(void) //****************************************************** void XGUI_Workshop::startApplication() { + DBG_FUN(); //Initialize event listening myEventsListener->initializeEventListening(); @@ -700,6 +713,28 @@ void XGUI_Workshop::showHelpPage(const QString& thePage) const } +//****************************************************** +void XGUI_Workshop::setBackupState(bool doBackup/*=true*/) +{ + DBG_FUN(); + ARG(doBackup); + myDoBackup = doBackup; +} + + +//****************************************************** +void XGUI_Workshop::setWaitForBackup(bool theDoWait/*=true*/) +{ + DBG_FUN(); + ARG(theDoWait); + myWaitForBackup = theDoWait; + if (theDoWait) + { + std::cout << "..... waiting for operation to finish to start backup." << std::endl; + } +} + + //****************************************************** void XGUI_Workshop::deactivateActiveObject(const ObjectPtr& theObject, const bool theUpdateViewer) { @@ -856,6 +891,9 @@ void XGUI_Workshop::connectToPropertyPanel(const bool isToConnect) //****************************************************** void XGUI_Workshop::onOperationResumed(ModuleBase_Operation* theOperation) { + DBG_FUN(); + ARG(theOperation->getDescription()->operationId()); + setGrantedFeatures(theOperation); if (theOperation->getDescription()->hasXmlRepresentation()) { //!< No need for property panel @@ -876,6 +914,9 @@ void XGUI_Workshop::onOperationResumed(ModuleBase_Operation* theOperation) //****************************************************** void XGUI_Workshop::onOperationStopped(ModuleBase_Operation* theOperation) { + DBG_FUN(); + ARG(theOperation->getDescription()->operationId()); + updateCommandStatus(); ModuleBase_OperationFeature* aFOperation = dynamic_cast @@ -958,6 +999,10 @@ void XGUI_Workshop::setGrantedFeatures(ModuleBase_Operation* theOperation) //****************************************************** void XGUI_Workshop::saveDocument(const QString& theName, std::list& theFileNames) { + DBG_FUN(); + ARG(theName); + ARG(theFileNames); + SHOW(myDoBackup); QApplication::restoreOverrideCursor(); SessionPtr aMgr = ModelAPI_Session::get(); @@ -976,12 +1021,15 @@ void XGUI_Workshop::saveDocument(const QString& theName, std::list& //****************************************************** bool XGUI_Workshop::abortAllOperations() { + DBG_FUN(); return myOperationMgr->abortAllOperations(); } //****************************************************** void XGUI_Workshop::operationStarted(ModuleBase_Operation* theOperation) { + DBG_FUN(); + ARG(theOperation->getDescription()->operationId()); setGrantedFeatures(theOperation); if (!theOperation->getDescription()->hasXmlRepresentation()) { //!< No need for property panel updateCommandStatus(); @@ -994,6 +1042,7 @@ void XGUI_Workshop::operationStarted(ModuleBase_Operation* theOperation) //****************************************************** void XGUI_Workshop::onOpen() { + DBG_FUN(); if(!abortAllOperations()) return; //save current file before close if modified @@ -1024,6 +1073,8 @@ void XGUI_Workshop::onOpen() //****************************************************** void XGUI_Workshop::openFile(const QString& theDirectory) { + DBG_FUN(); + ARG(theDirectory); myCurrentFile = theDirectory; if (myCurrentFile.isEmpty()) return; @@ -1196,7 +1247,10 @@ void XGUI_Workshop::onTrihedronVisibilityChanged(bool theState) //****************************************************** bool XGUI_Workshop::onSave() { - if(!myOperationMgr->abortAllOperations(XGUI_OperationMgr::XGUI_InformationMessage)) + DBG_FUN(); + SHOW(myDoBackup); + + if(!myDoBackup && !myOperationMgr->abortAllOperations(XGUI_OperationMgr::XGUI_InformationMessage)) return false; if (myCurrentFile.isEmpty()) { return onSaveAs(); @@ -1228,6 +1282,8 @@ bool XGUI_Workshop::onSave() //****************************************************** bool XGUI_Workshop::onSaveAs() { + DBG_FUN(); + SHOW(myDoBackup); if(!myOperationMgr->abortAllOperations(XGUI_OperationMgr::XGUI_InformationMessage)) return false; qreal aRatio = ModuleBase_Tools::currentPixelRatio(); @@ -1446,6 +1502,8 @@ void XGUI_Workshop::onExportPart() //****************************************************** ModuleBase_IModule* XGUI_Workshop::loadModule(const QString& theModule) { + DBG_FUN(); + ARG(theModule); QString libName = QString::fromStdString(library(theModule.toStdString())); if (libName.isEmpty()) { qWarning(qPrintable(tr("Information about module \"%1\" doesn't exist.").arg(theModule))); @@ -2355,10 +2413,12 @@ bool hasResults(QObjectPtrList theObjects, const std::set& theTypes // all nested parts. std::list allFeatures(const DocumentPtr& theDocument) { + DBG_FUNC(); std::list aResultList; std::list anAllFeatures = theDocument->allFeatures(); foreach (const FeaturePtr& aFeature, anAllFeatures) { // The order of appending features of the part and the part itself is important + MSGEL("* aFeature = [" << aFeature->getKind() << "] action=" << aFeature->isAction()); // Append features from a part feature std::list aResults; diff --git a/src/XGUI/XGUI_Workshop.h b/src/XGUI/XGUI_Workshop.h index b6e25c670..676a44d46 100644 --- a/src/XGUI/XGUI_Workshop.h +++ b/src/XGUI/XGUI_Workshop.h @@ -354,6 +354,12 @@ Q_OBJECT void showHelpPage(const QString& thePage) const; + void setBackupState(bool theDoBackup=true); + + void setWaitForBackup(bool theDoWait=true); + + bool waitForBackup() const { return myWaitForBackup; } + signals: /// Emitted when selection happens in Salome viewer void salomeViewerSelection(); @@ -592,6 +598,8 @@ private: Config_DataModelReader* myDataModelXMLReader; ///< XML reader of data model XGUI_InspectionPanel* myInspectionPanel; ///< container of feature attributes widgets QTemporaryDir myTmpDir; ///< a direcory for uncompressed files + bool myDoBackup; ///< whether the current save was initiated by automatic backup + bool myWaitForBackup; ///< whether to do a backup when current operation finishes }; #endif diff --git a/test.hdfs/testme.py b/test.hdfs/testme.py index 3a8965943..b8e3ae4d4 100644 --- a/test.hdfs/testme.py +++ b/test.hdfs/testme.py @@ -64,7 +64,7 @@ if __name__ == '__main__': with open(testlogfile, 'r') as inputFile: s = inputFile.read() - #print("logfile: ", s) + print("logfile: ", s) if s.find("FAIL") > 0: isOk = False error = s -- 2.39.2