import os
import SalomePyQt
import ModelAPI
-#import PartSetAPI
-#from salome.shaper import model
+from salome.shaper import model
-errCode = 0
-
-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:
+#------------------------------------------------------------------------
+def validateSession(logFile):
+ """
+ Iterate through and write all feature names of each part in the active study
+ to the logFile.
+ """
+ errCode = 0
try:
- 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")
+ logFile.write("getting SHAPER session\n")
session = ModelAPI.ModelAPI_Session.get()
- aFactory = session.validators()
+ # aFactory = session.validators()
+ logFile.write("getting PartSet\n")
aPartSet = session.moduleDocument()
numParts = aPartSet.size("Parts")
for partIdx in range(numParts+1):
continue
aPart.setActive(True)
if partIdx == 0:
- f.write("---PartSet:------------------\n")
+ logFile.write("---PartSet:------------------\n")
else:
- f.write(f"---Part_{partIdx}:------------------\n")
-
- ## Simulate an exception during execution:
- ##raise Exception("study failed to validate")
- ## Cause an exception
- #x = 1/0
+ logFile.write(f"---Part_{partIdx}:------------------\n")
for aFeat in aPart.allFeatures():
if aFeat.isInHistory():
- f.write(" * {} --> [{}]\n".format(aFeat.getKind(), aFeat.data().name()))
+ logFile.write(" * {} --> [{}]\n".format(aFeat.getKind(), aFeat.data().name()))
+ else:
+ logFile.write(" - {}\n".format(aFeat.data().name()))
+ except:
+ errCode = 128
+
+ return errCode
+
+
+#------------------------------------------------------------------------
+def checkHDF(logFile, hdfFile):
+ """
+ Open and validate a HDF document
+ """
+ errCode = 0
+ try:
+ logFile.write("-----CHECKING HDF DOCUMENT-------\n")
+ salome.standalone()
+ logFile.write("initializing SALOME\n")
+ salome.salome_init(hdfFile, embedded=True, forced=True)
+ logFile.write("StudyName = {}\n".format(salome.myStudyName))
+
+ logFile.write("getting KERNEL session\n")
+ session = salome.naming_service.Resolve('/Kernel/Session')
+ logFile.write("connecting to study\n")
+ session.emitMessage("connect_to_study")
+
+ logFile.write("activating SHAPER module\n")
+ sg = SalomePyQt.SalomePyQt()
+ sg.activateModule("Shaper")
+
+ errCode = validateSession(logFile)
+ except:
+ errCode = 129
+
+ return errCode
+
+
+#------------------------------------------------------------------------
+def checkPyScript(logFile, pyFile):
+ """
+ Load and validate a Python script
+ """
+ errCode = 0
+ try:
+ logFile.write("-----CHECKING PYTHON SCRIPT-------\n")
+ logFile.write("executing dumped script\n")
+ exec(compile(open(pyFile, 'rb').read(), pyFile, 'exec'))
+
+ errCode = validateSession(logFile)
+ except:
+ errCode = 130
+
+ return errCode
+
+
+#------------------------------------------------------------------------
+def validateBackup(fullName):
+ """
+ Open the backed up file and validate its content.
+ This test script will be called once for the stored HDF file
+ end once for the dumped Python script.
+ """
+ errCode = 0
+ try:
+ outFolder,filename = os.path.split(fullName)
+ base,ext = os.path.splitext(filename)
+ ext = ext[1:].lower() # remove the dot from the extension
+
+ # Create a log file in the backup folder starting with the same base name
+ logFile = os.path.join(outFolder, base+"_valid.log")
+ if os.path.exists(logFile):
+ append_write = 'a' # append if already exists
+ else:
+ append_write = 'w' # make a new file if not
+
+ with open(logFile, append_write) as f:
+ try:
+ f.write(f"fullName = {fullName}\n")
+ if ext == "hdf":
+ errCode = checkHDF(f, fullName)
+ elif ext == "py":
+ errCode = checkPyScript(f, fullName)
else:
- f.write(" - {}\n".format(aFeat.data().name()))
- except Exception as ex:
- f.write("Exception caught: {}\n".format(ex))
- errCode = 88
-
- f.write("errCode = {}\n".format(errCode))
- if errCode == 0:
- f.write("HDF Test - PASSED\n")
+ f.write(f"-----UNSUPPORTED FILE TYPE [{ext}]-------\n")
+ errCode = 131
+ f.write(f"errCode = {errCode}\n")
+ if errCode == 0:
+ f.write("{} Test - PASSED\n\n".format(ext.upper()))
+ else:
+ f.write("{} Test - FAILED\n\n".format(ext.upper()))
+ except:
+ errCode = 132
+ f.write("Exception caught\n")
+ except:
+ errCode = 133
+
+ return errCode
+
+
+#------------------------------------------------------------------------
+errCode = 0
+try:
+ if (len(sys.argv) != 2):
+ errCode = 134
else:
- f.write("HDF Test - FAILED\n")
+ fullName = sys.argv[1]
+ errCode = validateBackup(fullName)
+except:
+ errCode = 135
exit(errCode)
#include <QToolBar>
#include <QFileInfo>
#include <QDir>
+#include <QMessageBox>
#include <chrono>
#include <future>
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) ));
+ connect( this, SIGNAL( backupDone(QString,int) ), this, SLOT( onBackupDone(QString,int) ));
// It will be called in XGUI_Workshop::startApplication
// ModuleBase_Preferences::loadCustomProps();
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 );
+ bool useBackup = aResMgr->booleanValue( ModuleBase_Preferences::GENERAL_SECTION, "use_auto_backup", true );
if (useBackup) {
- int backupInterval = aResMgr->integerValue( "General", "backup_interval", 1 );
+ int backupInterval = aResMgr->integerValue( ModuleBase_Preferences::GENERAL_SECTION, "backup_interval", 5 );
if ( backupInterval > 0 ){
#ifdef DBG_BACKUP_INTERVAL
backupInterval = DBG_BACKUP_INTERVAL; // MBS: use shorter interval for debugging
};
//******************************************************
-bool SHAPERGUI::backupDoc()
+int SHAPERGUI::backupDoc()
{
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;
+ return 32;
}
+ int aResult = 0;
bool isOk = false;
SUIT_Study* study = application()->activeStudy();
if ( !study ) {
myBackupError = tr("There is no active study");
- return false;
+ return 33;
}
LockBackupState lockBackup(myWorkshop);
SHOW(aName);
if ( aName.isEmpty() ) {
myBackupError = tr("Study name is empty");
- return false;
+ return 34;
}
const QChar aSep = QDir::separator();
- //MBS:
SUIT_ResourceMgr* aResMgr = application()->resourceMgr();
if ( aResMgr && application()->activeStudy() ) {
- aFolder = aResMgr->path( "General", "backup_folder", "" );
+ aFolder = aResMgr->path( ModuleBase_Preferences::GENERAL_SECTION, "backup_folder", "" );
}
if (aFolder.isEmpty()) {
#ifdef HAVE_SALOME
aDir.mkdir(aFolder);
if (!aDir.exists()) {
myBackupError = tr("Cannot create backup folder");
- return false;
+ return 35;
}
}
isOk = study->saveDocumentAs( aFullName, true );
if (!isOk){
myBackupError = tr("Cannot backup study document");
- return false;
+ return 36;
}
// Now, dump the python script
LightApp_Study *lightStudy = dynamic_cast<LightApp_Study*>(study);
if (!lightStudy) {
myBackupError = tr("Study is not dumpable");
- return false;
+ return 37;
}
aFullName = aFolder + aSep + aName + QString(".py");
isOk = lightStudy->dump(aFullName, true, false, false);
if (!isOk){
myBackupError = tr("Cannot backup python script");
- return false;
+ return 38;
}
// Finally start another salome process and reload the saved document & script for verification
<< QString("salome")
<< testBackup;
QString testScript = dirs.join( QDir::separator() );
- int aResult = checkBackup.run(testScript);
- isOk = (aResult == 0);
+ aResult = checkBackup.run(testScript);
}
catch (std::exception &ex)
{
myBackupError = tr("std::exception caught");
- isOk = false;
+ aResult = 39;
}
catch (...)
{
myBackupError = tr("unknown exception caught");
- isOk = false;
+ aResult = 40;
}
MSGEL("...emit backupDone signal");
SHOW(aFolder);
- SHOW(isOk);
- emit backupDone(aFolder, isOk);
- return isOk;
+ SHOW(aResult);
+ emit backupDone(aFolder, aResult);
+ return aResult;
}
//******************************************************
-void SHAPERGUI::onBackupDone(QString aFolder, bool aResult)
+void SHAPERGUI::onBackupDone(QString aFolder, int aResult)
{
DBG_FUN();
ARG(aFolder);
ARG(aResult);
- bool isOk = myBackupResult.get();
- SHOW(isOk);
- putInfo( isOk ? tr("Backup done in folder: %1").arg(aFolder) : tr("Failed to backup active study!"), 5000 );
+ int aErrCode = myBackupResult.get();
+ SHOW(aErrCode);
+ bool isOk = (aResult == 0);
+ if (isOk)
+ {
+ putInfo(tr("Backup done in folder: %1").arg(aFolder), 5000 );
+ }
+ else
+ {
+ QString aMsg = tr("Failed to backup active study!\nError Code: %1").arg(aResult);
+ QMessageBox::warning(application()->desktop(), tr("Automatic Backup"), aMsg);
+ }
int aBackupStorage{-1};
SUIT_ResourceMgr* aResMgr = application()->resourceMgr();
- if ( aResMgr ) {
- aBackupStorage = aResMgr->integerValue( "General", "auto_backup_storage", -1);
+ if ( aResMgr )
+ {
+ aBackupStorage = aResMgr->integerValue( ModuleBase_Preferences::GENERAL_SECTION, "auto_backup_storage", -1);
+ }
+ else
+ {
+ MSGEL("ERR: could not get resource manager");
}
- if (aBackupStorage == 0/*StoreLastBackupOnly*/) {
+ if (aBackupStorage == 0/*StoreLastBackupOnly*/)
+ {
+ MSGEL("===> only keep the latest successful backup: create " << aFolder.toStdString());
// Only keep the latest successful backup => delete the previous one, if it exists
- if (isOk && !myLastBackupFolder.isEmpty()) {
+ 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() <= 5) {
+ // I am afraid of accidently removing an entire folder tree, therefore check
+ // if "dir" contains really the latest backups and nothing more
+ if (!files.isEmpty() && files.length() <= 4)
+ {
QString baseName = files.constFirst();
baseName = baseName.left(baseName.lastIndexOf('.'));
- if (!baseName.isEmpty() && files.filter(baseName).length() == files.length()) {
+ if (!baseName.isEmpty() && files.filter(baseName).length() == files.length())
+ {
MSGEL("........removing old backup folder");
- dir.removeRecursively();
+ const bool success = dir.removeRecursively();
+ if (!success)
+ {
+ QString aMsg = tr("The previous backup folder could not be removed!");
+ QMessageBox::warning(application()->desktop(), tr("Automatic Backup"), aMsg);
+ }
}
}
+ else
+ {
+ QString aMsg = tr("The previous backup folder was not deleted,\nas there are more files in it than it is expected!");
+ QMessageBox::warning(application()->desktop(), tr("Automatic Backup"), aMsg);
+ }
}
myLastBackupFolder = aFolder;
}
+ else
+ {
+ MSGEL("===> keep entire backup history: adding " << aFolder.toStdString());
+ }
// 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 ){
+ if ( aResMgr && application()->activeStudy() )
+ {
+ bool useBackup = aResMgr->booleanValue( ModuleBase_Preferences::GENERAL_SECTION, "use_auto_backup", true );
+ if (useBackup)
+ {
+ int backupInterval = aResMgr->integerValue( ModuleBase_Preferences::GENERAL_SECTION, "backup_interval", 5 );
+ if ( backupInterval > 0 )
+ {
#ifdef DBG_BACKUP_INTERVAL
backupInterval = DBG_BACKUP_INTERVAL; // MBS: use shorter interval for debugging
#endif
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 );
+ int backupInterval = aResMgr->integerValue( ModuleBase_Preferences::GENERAL_SECTION, "backup_interval", 5 );
if ( backupInterval > 0 ){
#ifdef DBG_BACKUP_INTERVAL
backupInterval = DBG_BACKUP_INTERVAL; // MBS: use shorter interval for debugging
// update SHAPERSTUDY objects in OCC and VTK viewers
QStringList aVMList;
aVMList << "OCCViewer" << "VTKViewer";
+ MSGEL("publishToStudy() : updatePresentations(SHAPERSTUDY) start.");
getApp()->updatePresentations("SHAPERSTUDY", aVMList);
+ MSGEL("publishToStudy() : updatePresentations(SHAPERSTUDY) end.");
}
}
virtual void updateInfoPanel();
signals:
- void backupDone(QString aName, bool aResult);
+ void backupDone(QString aName, int aResult);
public slots:
/// \brief The method is redefined to connect to the study viewer before the data
void onBackupDoc();
/// Document has been backed-up
- void onBackupDone(QString aName, bool aResult);
+ void onBackupDone(QString aName, int aResult);
/// Obtains the current application and updates its actions
void onUpdateCommandStatus();
virtual bool abortAllOperations();
/// The automatic backup thread function
- bool backupDoc();
+ int backupDoc();
private slots:
void onWhatIs(bool isToggled);
double myAxisArrowRate;
/// Automatic backup
- QTimer* myBackupTimer; // The timer which triggers the automatic backup
- std::future<bool> myBackupResult; // The result (succes/failure) of the backup
- QString myBackupError; // The backup error message in case of a failure
- QString myLastBackupFolder; // The folder of the last backup
+ QTimer* myBackupTimer; // The timer which triggers the automatic backup
+ std::future<int> myBackupResult; // The result (succes/failure) of the backup
+ QString myBackupError; // The backup error message in case of a failure
+ QString myLastBackupFolder; // The folder of the last backup
};
#endif
int aResult = 0;
if (myProcess)
- return 1;
+ return 64;
QString aProgName = std::getenv("PYTHONBIN");
if (aProgName.isEmpty())
myProcess = new QProcess(this);
if (!myProcess)
- return 2;
+ return 65;
// connect(myProcess, SIGNAL(started()), this, SLOT(procStarted()));
// connect(myProcess, SIGNAL(error(QProcess::ProcessError)), this,
// * opens the previously backed up HDF study
SHOW(aProgName);
SHOW(args);
- //myProcess->setStandardOutputFile((QStringList() << myFolder << myBaseName+".log").join(aSep));
myProcess->start(aProgName, args);
myProcess->waitForFinished(300000);
- int exitStat = myProcess->exitStatus();
+ QProcess::ExitStatus exitStat = myProcess->exitStatus(); // 0=NormalExit, 1=CrashExit
SHOW(exitStat);
int exitCode = myProcess->exitCode();
SHOW(exitCode);
- if (exitStat == 0/*NormalExit*/ && exitCode != 0)
+ if (exitStat == QProcess::NormalExit && exitCode != 0)
aResult = exitCode;
- else if (exitStat == -1/*CrashExit*/)
- aResult = 99;
+ else if (exitStat == QProcess::CrashExit)
+ aResult = 66;
myProcess->deleteLater();
// Check the output of the log file
- if (exitStat == 0 && exitCode == 0)
+ if (aResult == 0)
{
std::ifstream log((QStringList() << myFolder << myBaseName+"_valid.log").join(aSep).toStdString().c_str());
if (log)
{
- aResult = 76;
+ uint8_t testFlag = 0;
std::string line;
while (std::getline(log, line))
{
if (line.find("HDF Test - ") == 0)
{
+ testFlag |= 0x1; // 0x01 = HDF Test performed
std::string strResult = line.substr(11);
if (strResult.find("PASSED") == 0)
{
MSGEL("HDF Test --> PASSED");
- aResult = 0;
+ if (testFlag == 0x03)
+ break;
}
else if (strResult.find("FAILED") == 0)
{
MSGEL("HDF Test --> FAILED");
- aResult = 75;
+ aResult = 67;
+ break;
}
- break;
+ else
+ {
+ MSGEL("HDF Test --> unknown result");
+ aResult = 68;
+ break;
+ }
+ }
+ else if (line.find("PY Test - ") == 0)
+ {
+ testFlag |= 0x2; // 0x02 = PY Test performed
+ std::string strResult = line.substr(10);
+ if (strResult.find("PASSED") == 0)
+ {
+ MSGEL("PY Test --> PASSED");
+ if (testFlag == 0x03)
+ break;
+ }
+ else if (strResult.find("FAILED") == 0)
+ {
+ MSGEL("PY Test --> FAILED");
+ aResult = 69;
+ break;
+ }
+ else
+ {
+ MSGEL("PY Test --> unknown result");
+ aResult = 70;
+ break;
+ }
+ }
+ }
+ if (aResult == 0 && testFlag != 0x03)
+ {
+ // Not all tests were performed or they were interrupted
+ switch (testFlag)
+ {
+ case 0x00: MSGEL("None of the tests were performed until the end.");
+ aResult = 71;
+ break;
+ case 0x01: MSGEL("The PY Test was not performed until the end.");
+ aResult = 72;
+ break;
+ case 0x02: MSGEL("The HDF Test was not performed until the end.");
+ aResult = 73;
+ break;
}
}
}
else
{
std::cout << "WARNING: cannot open log file from check_validity.py script" << std::endl;
- aResult = 77; // log file not found
+ aResult = 74; // log file not found
}
}
# 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.")
+#------------------------------------------------------------------------
+def checkFileInSubprocess(logFile, title, args):
+ errCode = 0
+ try:
+ logFile.write(f"starting {title} process:\n")
+ logFile.write(" cmd_line = '{}'\n".format(" ".join(args)))
+ proc = subprocess.Popen(args)
+ try:
+ logFile.write(f" start communication with {title}\n")
+ proc.communicate(timeout = 500)
+ logFile.write(f"{title} terminated\n")
+ errCode = proc.returncode
+ logFile.write(f"{title} returned: {errCode}\n")
+ except subprocess.TimeoutExpired:
+ errCode = 96
+ logFile.write(f"{title} timed out\n")
+ except Exception as ex:
+ errCode = 97
+ logFile.write(f"Exception caught: {ex}\n")
+ except:
+ errCode = 98
+ logFile.write("Unknown Exception caught\n")
-backupFolder = sys.argv[1]
-baseName = sys.argv[2]
-checkScript = sys.argv[3]
+ logFile.write(f"errCode = {errCode}\n")
+ except:
+ errCode = 99
-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])
+ return errCode
+
+
+#------------------------------------------------------------------------
+def checkBackups(backupFolder, baseName, checkScript):
+ errCode = 0
try:
- f.write("start communication with SALOME\n")
- proc.communicate(timeout = testTimeout)
- f.write("SALOME terminated\n")
- errCode = proc.returncode
- f.write("SALOME returned: {}\n".format(errCode))
- except subprocess.TimeoutExpired:
- errCode = 99
- f.write("SALOME timed out\n")
- except Exception as ex:
- errCode = 33
- f.write("Exception caught: {}\n".format(ex))
-
- f.write("errCode = {}\n".format(errCode))
- if errCode == 0:
- f.write("Backup Test - PASSED\n")
+ # Create a log file in the backup folder starting with the same base name
+ with open(os.path.join(backupFolder, baseName+"_test.log"), "w") as logFile:
+ logFile.write("test_backup script started\n")
+ logFile.write(f" backupFolder = {backupFolder}\n")
+ logFile.write(f" baseName = {baseName}\n")
+ logFile.write(f" checkScript = {checkScript}\n")
+ hdfFile = os.path.join(backupFolder, baseName+".hdf")
+ logFile.write(f" hdfFile = {hdfFile}\n")
+
+ args = ["runSalome.py", "--modules", "SHAPER,SHAPERSTUDY", "--batch", "--splash", "0", checkScript, "args:" + hdfFile]
+ errCode = checkFileInSubprocess(logFile, "SALOME", args)
+ logFile.write(f"errCode = {errCode}\n")
+
+ if errCode == 0:
+ pyFile = os.path.join(backupFolder, baseName+".py")
+ logFile.write(f" pyfile = {pyFile}\n")
+
+ args = ["python", checkScript, pyFile]
+ errCode = checkFileInSubprocess(logFile, "PYTHON", args)
+ logFile.write(f"errCode = {errCode}\n")
+
+ except:
+ errCode = 100
+
+ return errCode
+
+
+#------------------------------------------------------------------------
+errCode = 0
+try:
+ if (len(sys.argv) != 4):
+ errCode = 101
else:
- f.write("Backup Test - FAILED\n")
+ backupFolder = sys.argv[1]
+ baseName = sys.argv[2]
+ checkScript = sys.argv[3]
+ errCode = checkBackups(backupFolder, baseName, checkScript)
+except:
+ errCode = 102
-exit(errCode)
\ No newline at end of file
+exit(errCode)