--- /dev/null
+import salome
+import sys
+import os
+import SalomePyQt
+import ModelAPI
+#import PartSetAPI
+#from salome.shaper import model
+
+
+if len(sys.argv) < 2:
+ raise Exception("no study given to validate")
+
+outFolder,filename = os.path.split(sys.argv[1])
+base = os.path.splitext(filename)[0]
+
+with open(os.path.join(outFolder, base+"_valid.log"), "w") as f:
+ f.write("#args = {}\n".format(len(sys.argv)))
+ lastIdx = len(sys.argv)-1
+ f.write("#arg[{}] = {}\n".format(lastIdx, sys.argv[lastIdx]))
+
+ salome.standalone()
+ f.write("initializing SALOME\n")
+ salome.salome_init(sys.argv[1], embedded=True, forced=True)
+ f.write("StudyName = {}\n".format(salome.myStudyName))
+ f.write("Study = {}\n".format(salome.myStudy))
+
+ session = salome.naming_service.Resolve('/Kernel/Session')
+ f.write("session = {}\n".format(session))
+ session.emitMessage("connect_to_study")
+ f.write("session = {}\n".format(session))
+
+ sg = SalomePyQt.SalomePyQt()
+ sg.activateModule("Shaper")
+ session = ModelAPI.ModelAPI_Session.get()
+ aFactory = session.validators()
+ aPartSet = session.moduleDocument()
+ numParts = aPartSet.size("Parts")
+ # f.write("NumParts = {}\n".format(numParts))
+ # numFeats = aPartSet.size("Features")
+ # f.write("NumFeats = {}\n".format(numFeats))
+ # numBodies = aPartSet.size("Bodies")
+ # f.write("NumBodies = {}\n".format(numBodies))
+ # numGroups = aPartSet.size("Groups")
+ # f.write("NumGroups = {}\n".format(numGroups))
+ # numConsts = aPartSet.size("Construction")
+ # f.write("NumConstr = {}\n".format(numConsts))
+ # numFolders = aPartSet.size("Folders")
+ # f.write("NumFolder = {}\n".format(numFolders))
+ for partIdx in range(numParts+1):
+ aPart = session.document(partIdx)
+ if aPart is None:
+ continue
+ aPart.setActive(True)
+ if partIdx == 0:
+ f.write("---PartSet:------------------\n")
+ else:
+ f.write(f"---Part_{partIdx}:------------------\n")
+ for aFeat in aPart.allFeatures():
+ if aFeat.isInHistory():
+ f.write(" * {} --> [{}]\n".format(aFeat.getKind(), aFeat.data().name()))
+ else:
+ f.write(" - {}\n".format(aFeat.data().name()))
+
+ f.write("done!\n")
+
+exit()
#include <XGUI_FacesPanel.h>
#include <XGUI_SelectionActivate.h>
#include <XGUI_InspectionPanel.h>
+#include <XGUI_Tools.h>
#include <XGUI_ViewerProxy.h>
#include <ModuleBase_Operation.h>
#define MBCLASSNAME "SHAPERGUI"
#include "MBDebug.h"
// <-- insert includes for addtional debug headers here!
+//#define DBG_BACKUP_INTERVAL 1 /*Use a short 1 min interval for auto backup for debugging*/
//---------------------------------------------------------
#if OCC_VERSION_HEX < 0x070400
};
-// static void DumpOperations(XGUI_Workshop *wshop)
-// {
-// XGUI_OperationMgr *aOpMgr = wshop->operationMgr();
-// if (!aOpMgr) return;
-// QStringList operations = aOpMgr->operationList();
-// SHOW(operations);
-// }
-
//******************************************************
SHAPERGUI::SHAPERGUI()
if (useBackup) {
int backupInterval = aResMgr->integerValue( "General", "backup_interval", 1 );
if ( backupInterval > 0 ){
- backupInterval = 1;
+#ifdef DBG_BACKUP_INTERVAL
+ backupInterval = DBG_BACKUP_INTERVAL; // MBS: use shorter interval for debugging
+#endif
MSGEL("....starting BackupTimer: interval=" << backupInterval);
myBackupTimer->start( backupInterval*60000 );
}
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();
- }
+ checkForWaitingBackup();
}
//******************************************************
onOperationGeneric(theOperation, moduleName(), "aborted");
+ checkForWaitingBackup();
+}
+
+//******************************************************
+void SHAPERGUI::checkForWaitingBackup()
+{
if (myWorkshop && myWorkshop->waitForBackup())
{
XGUI_OperationMgr *operMgr = myWorkshop->operationMgr();
DBG_FUN();
MSGEL(" ...backing up current study");
+ // We cannot save the study while we are still in an ongoing operation
+ // => so test for this case first and delay the backup to the time when operation finishes
if (myWorkshop && myWorkshop->operationMgr())
{
if (myWorkshop->operationMgr()->hasOperation())
{
DBG_FUN();
+ if (myWorkshop->backupState()) {
+ // This should never happen as I restart the backup timer only when a backup has finished
+ myBackupError = tr("Another backup is still running");
+ return false;
+ }
+
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 )
+ if ( !study ) {
+ myBackupError = tr("There is no active study");
return false;
-
- // if ( !abortAllOperations() )
- // return false;
+ }
LockBackupState lockBackup(myWorkshop);
- QString aName = study->studyName();
+ QString aFolder{};
try
{
+ QString aName = study->studyName();
SHOW(aName);
- if ( aName.isNull() )
+ if ( aName.isEmpty() ) {
+ myBackupError = tr("Study name is empty");
return false;
+ }
const QChar aSep = QDir::separator();
-
- QString aFolder("/home/bernhard/backups");
+ //MBS:
+ SUIT_ResourceMgr* aResMgr = application()->resourceMgr();
+ if ( aResMgr && application()->activeStudy() ) {
+ aFolder = aResMgr->path( "General", "backup_folder", "" );
+ }
+ if (aFolder.isEmpty()) {
+#ifdef HAVE_SALOME
+ aFolder = XGUI_Tools::getTmpDirByEnv("SALOME_TMP_DIR").c_str();
+#else
+ aFolder = XGUI_Tools::getTmpDirByEnv("").c_str();
+#endif
+ }
+ if (aFolder.endsWith(aSep))
+ aFolder = aFolder.remove(aFolder.length()-1,1);
QDateTime now = QDateTime::currentDateTime();
aFolder += aSep + now.toString("yyyyMMdd_hhmmss");
if (!aDir.exists()) {
aDir.mkpath(aFolder);
aDir.mkdir(aFolder);
+ if (!aDir.exists()) {
+ myBackupError = tr("Cannot create backup folder");
+ return false;
+ }
}
if (study->isSaved()) {
// Save the study into a single HDF file
isOk = study->saveDocumentAs( aFullName, true );
if (!isOk){
- MSGEL("ERR: failed to backup study document");
- return isOk;
+ myBackupError = tr("Cannot backup study document");
+ return false;
}
// Now, dump the python script
LightApp_Study *lightStudy = dynamic_cast<LightApp_Study*>(study);
- if (!lightStudy)
+ if (!lightStudy) {
+ myBackupError = tr("Study is not dumpable");
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;
+ myBackupError = tr("Cannot backup python script");
+ return false;
}
// Finally start another salome process and reload the saved document & script for verification
+ SHOW(aFolder);
SHAPERGUI_CheckBackup checkBackup(aFolder, aName);
- QString testScript("/home/bernhard/backups/checkValidity.py");
+ QString testBackup("check_validity.py");
+ QStringList dirs;
+ dirs << QString(std::getenv("SHAPER_ROOT_DIR"))
+ << QString("bin")
+ << QString("salome")
+ << testBackup;
+ QString testScript = dirs.join( QDir::separator() );
int aResult = checkBackup.run(testScript);
isOk = (aResult == 0);
}
catch (std::exception &ex)
{
- std::cout << "ERROR: std::exception caught" << std::endl;
+ myBackupError = tr("std::exception caught");
isOk = false;
}
catch (...)
{
- std::cout << "ERROR: unknown exception caught" << std::endl;
+ myBackupError = tr("unknown exception caught");
isOk = false;
}
- #endif
+
MSGEL("...emit backupDone signal");
- emit backupDone(aName, isOk);
+ SHOW(aFolder);
+ SHOW(isOk);
+ emit backupDone(aFolder, isOk);
return isOk;
}
//******************************************************
-void SHAPERGUI::onBackupDone(QString aName, bool aResult)
+void SHAPERGUI::onBackupDone(QString aFolder, bool aResult)
{
DBG_FUN();
- ARG(aName);
+ ARG(aFolder);
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 );
+ putInfo( isOk ? tr("Backup done in folder: %1").arg(aFolder) : tr("Failed to backup active study!"), 5000 );
+
+ int aBackupStorage{-1};
+ SUIT_ResourceMgr* aResMgr = application()->resourceMgr();
+ if ( aResMgr ) {
+ aBackupStorage = aResMgr->integerValue( "General", "auto_backup_storage", -1);
+ }
+ if (aBackupStorage == 0/*StoreLastBackupOnly*/) {
+ // Only keep the latest successful backup => delete the previous one, if it exists
+ if (isOk && !myLastBackupFolder.isEmpty()) {
+ // Delete the previous backup folder
+ // To avoid deleting accidently an incorrect folder, check for
+ // the correct content. A backup folder should have 3-5 files:
+ // * <FileName>.hdf - the study itself
+ // * <FileName>.log - the output from the additional SALOME instance
+ // * <FileName>.py - the python dump
+ // * <FileName>_test.log - the output of the "test_backup.py" script
+ // * <FileName>_valid.log - the output of the "check_validity.py" script
+ QDir dir(myLastBackupFolder);
+ QStringList files = dir.entryList(QDir::Files|QDir::Dirs|QDir::NoDotAndDotDot);
+ if (!files.isEmpty() && files.length() <= 3) {
+ QString baseName = files.constFirst();
+ baseName = baseName.left(baseName.lastIndexOf('.'));
+ if (!baseName.isEmpty() && files.filter(baseName).length() == files.length())
+ dir.removeRecursively();
+ }
+ }
+ myLastBackupFolder = aFolder;
+ }
+
+ // Start the timer again
+ 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 ){
+#ifdef DBG_BACKUP_INTERVAL
+ backupInterval = DBG_BACKUP_INTERVAL; // MBS: use shorter interval for debugging
+#endif
+ MSGEL("....starting BackupTimer: interval=" << backupInterval);
+ myBackupTimer->start( backupInterval*60000 );
+ }
+ }
+ }
}
//******************************************************
--- /dev/null
+#!/usr/bin/env python3
+
+# Copyright (C) 2023 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
+#
+
+
+import subprocess
+import sys, os
+import tempfile
+
+testTimeout = 600
+
+# if len(sys.argv) != 4:
+# raise Exception("check_validity.py could not be found. Check your environment.")
+
+backupFolder = sys.argv[1]
+baseName = sys.argv[2]
+checkScript = sys.argv[3]
+
+errCode = 0
+with open(os.path.join(backupFolder, baseName+"_test.log"), "w") as f:
+ f.write("script started\n")
+ f.write(" backupFolder = {}\n".format(backupFolder))
+ f.write(" baseName = {}\n".format(baseName))
+ f.write(" checkScript = {}\n".format(checkScript))
+ hdffile = os.path.join(backupFolder, baseName+".hdf")
+ pyfile = os.path.join(backupFolder, baseName+".py")
+
+ f.write("starting SALOME process:\n")
+ proc = subprocess.Popen(["runSalome.py", "--modules", "SHAPER,SHAPERSTUDY", "--batch", "--splash", "0", checkScript, "args:" + hdffile])
+ try:
+ f.write("start communication with SALOME\n")
+ proc.communicate(timeout = testTimeout)
+ f.write("SALOME terminated\n")
+ except subprocess.TimeoutExpired:
+ errCode = 99
+ f.write("SALOME timed out\n")
+
+ f.write("\ndone.\n")
+
+exit(errCode)
\ No newline at end of file