mirror of
https://github.com/OPM/ResInsight.git
synced 2026-08-27 05:37:21 -05:00
Job improvements (#13895)
* Add job wait/queued state * Add support for limiting number of jobs running * Add job queue for jobs waiting to run
This commit is contained in:
@@ -38,6 +38,10 @@ RiaPreferencesOpm::RiaPreferencesOpm()
|
||||
CAF_PDM_InitFieldNoDefault( &m_opmFlowCommand, "opmFlowCommand", "Path to OPM Flow executable" );
|
||||
m_opmFlowCommand.uiCapability()->setUiEditorTypeName( caf::PdmUiFilePathEditor::uiEditorTypeName() );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_maxParallelJobs, "maxParallelJobs", "Maximum number of jobs to run in parallel" );
|
||||
m_maxParallelJobs = 1;
|
||||
m_maxParallelJobs.setRange( 1, 100 );
|
||||
|
||||
CAF_PDM_InitField( &m_useWsl, "useWsl", false, "Use WSL to run OPM Flow" );
|
||||
CAF_PDM_InitField( &m_useMpi, "useMpi", false, "Enable MPI" );
|
||||
|
||||
@@ -89,6 +93,8 @@ void RiaPreferencesOpm::appendItems( caf::PdmUiOrdering& uiOrdering )
|
||||
opmGrp->add( &m_mpirunCommand );
|
||||
}
|
||||
|
||||
opmGrp->add( &m_maxParallelJobs );
|
||||
|
||||
auto cmdGrp = uiOrdering.addNewGroup( "Default Command Line Settings" );
|
||||
m_jobSettings->uiOrdering( cmdGrp );
|
||||
}
|
||||
@@ -173,3 +179,11 @@ RimOpmFlowJobSettings* RiaPreferencesOpm::createDefaultJobSettings() const
|
||||
{
|
||||
return m_jobSettings->clone();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RiaPreferencesOpm::maxParallelJobs() const
|
||||
{
|
||||
return m_maxParallelJobs();
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ public:
|
||||
bool useWsl() const;
|
||||
bool useMpi() const;
|
||||
QString mpirunCommand() const;
|
||||
size_t maxParallelJobs() const;
|
||||
|
||||
RimOpmFlowJobSettings* createDefaultJobSettings() const;
|
||||
|
||||
@@ -57,6 +58,7 @@ private:
|
||||
caf::PdmField<bool> m_useMpi;
|
||||
caf::PdmField<QString> m_mpirunCommand;
|
||||
caf::PdmChildField<RimOpmFlowJobSettings*> m_jobSettings;
|
||||
caf::PdmField<size_t> m_maxParallelJobs;
|
||||
|
||||
QStringList m_availableWslDists;
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ set(SOURCE_GROUP_HEADER_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicDuplicateJobFeature.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicViewJobLogFeature.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicStopJobFeature.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicStopAllJobsFeature.h
|
||||
)
|
||||
|
||||
set(SOURCE_GROUP_SOURCE_FILES
|
||||
@@ -12,6 +13,7 @@ set(SOURCE_GROUP_SOURCE_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicDuplicateJobFeature.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicViewJobLogFeature.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicStopJobFeature.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicStopAllJobsFeature.cpp
|
||||
)
|
||||
|
||||
list(APPEND COMMAND_CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2026 Equinor ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight 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 General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RicStopAllJobsFeature.h"
|
||||
|
||||
#include "RiaGuiApplication.h"
|
||||
|
||||
#include "Jobs/RimGenericJob.h"
|
||||
#include "Jobs/RimJobCollection.h"
|
||||
|
||||
#include "cafSelectionManager.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QMessageBox>
|
||||
|
||||
CAF_CMD_SOURCE_INIT( RicStopAllJobsFeature, "RicStopAllJobsFeature" );
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicStopAllJobsFeature::onActionTriggered( bool isChecked )
|
||||
{
|
||||
if ( auto coll = dynamic_cast<RimJobCollection*>( caf::SelectionManager::instance()->selectedItem() ) )
|
||||
{
|
||||
stopAllJobs( coll );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicStopAllJobsFeature::setupActionLook( QAction* actionToSetup )
|
||||
{
|
||||
actionToSetup->setIcon( QIcon( ":/stop.svg" ) );
|
||||
actionToSetup->setText( "Stop..." );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicStopAllJobsFeature::stopAllJobs( RimJobCollection* coll )
|
||||
{
|
||||
if ( coll != nullptr )
|
||||
{
|
||||
if ( QMessageBox::question( RiaGuiApplication::widgetToUseAsParent(),
|
||||
"Stop All Jobs",
|
||||
"Do you want to stop all running and queued jobs?",
|
||||
QMessageBox::Yes | QMessageBox::No ) == QMessageBox::Yes )
|
||||
{
|
||||
for ( auto job : coll->jobs() )
|
||||
{
|
||||
if ( job->isRunning() ) job->stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2026 Equinor ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight 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 General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cafCmdFeature.h"
|
||||
|
||||
class RimJobCollection;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RicStopAllJobsFeature : public caf::CmdFeature
|
||||
{
|
||||
CAF_CMD_HEADER_INIT;
|
||||
|
||||
public:
|
||||
static void stopAllJobs( RimJobCollection* jobCollection );
|
||||
|
||||
protected:
|
||||
void onActionTriggered( bool isChecked ) override;
|
||||
void setupActionLook( QAction* actionToSetup ) override;
|
||||
};
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "RimJobMonitor.h"
|
||||
#include "RimProcess.h"
|
||||
#include "RimProcessQueue.h"
|
||||
|
||||
#include "RiuGuiTheme.h"
|
||||
|
||||
@@ -40,11 +41,10 @@ CAF_PDM_XML_ABSTRACT_SOURCE_INIT( RimGenericJob, "GenericJob" ); // Do not use.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimGenericJob::RimGenericJob()
|
||||
: m_percentageDone( 0.0 )
|
||||
, m_lastRunFailed( false )
|
||||
, m_isRunning( false )
|
||||
, m_process( nullptr )
|
||||
, m_jobState( JobState::Idle )
|
||||
, m_errorsDetected( 0 )
|
||||
, m_warningsDetected( 0 )
|
||||
, m_process( nullptr )
|
||||
{
|
||||
CAF_PDM_InitObject( "Generic Job" );
|
||||
}
|
||||
@@ -94,7 +94,15 @@ QString RimGenericJob::workingDirectory() const
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimGenericJob::isRunning() const
|
||||
{
|
||||
return m_isRunning;
|
||||
return ( m_jobState == JobState::Queued ) || ( m_jobState == JobState::Running );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimGenericJob::JobState RimGenericJob::state() const
|
||||
{
|
||||
return m_jobState;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -104,7 +112,7 @@ bool RimGenericJob::stop()
|
||||
{
|
||||
if ( !m_process.isNull() )
|
||||
{
|
||||
m_process->terminate();
|
||||
RimProcessQueue::stopProcess( m_process->ID() );
|
||||
RiaLogging::info( "Job \"" + name() + "\" stopped by user." );
|
||||
return true;
|
||||
}
|
||||
@@ -120,8 +128,11 @@ bool RimGenericJob::execute()
|
||||
|
||||
m_errorsDetected = 0;
|
||||
m_warningsDetected = 0;
|
||||
m_process = nullptr;
|
||||
m_percentageDone = 0.0;
|
||||
m_process = nullptr;
|
||||
m_jobState = JobState::Idle;
|
||||
|
||||
onProgress( m_percentageDone );
|
||||
|
||||
// job preparations
|
||||
{
|
||||
@@ -131,7 +142,7 @@ bool RimGenericJob::execute()
|
||||
|
||||
if ( !onPrepare() )
|
||||
{
|
||||
m_lastRunFailed = true;
|
||||
m_jobState = JobState::Failed;
|
||||
onProgress( m_percentageDone );
|
||||
return false;
|
||||
}
|
||||
@@ -141,18 +152,19 @@ bool RimGenericJob::execute()
|
||||
if ( !onRun() ) return false;
|
||||
|
||||
QStringList cmdLine = command();
|
||||
if ( cmdLine.isEmpty() ) return false;
|
||||
if ( cmdLine.isEmpty() )
|
||||
{
|
||||
m_jobState = JobState::Failed;
|
||||
onProgress( m_percentageDone );
|
||||
return false;
|
||||
}
|
||||
|
||||
// cannot delete job while running
|
||||
setDeletable( false );
|
||||
m_jobState = JobState::Queued;
|
||||
|
||||
m_process = new RimProcess( true, new RimJobMonitor( this ) );
|
||||
|
||||
m_isRunning = true;
|
||||
m_lastRunFailed = false;
|
||||
|
||||
onProgress( m_percentageDone );
|
||||
|
||||
// build process to run
|
||||
QString cmd = cmdLine.takeFirst();
|
||||
m_process->setCommand( cmd );
|
||||
@@ -163,20 +175,10 @@ bool RimGenericJob::execute()
|
||||
m_process->addEnvironmentVariable( name, value );
|
||||
}
|
||||
|
||||
// run process
|
||||
bool startOk = m_process->start();
|
||||
if ( !startOk )
|
||||
{
|
||||
onCompleted( false );
|
||||
m_lastRunFailed = true;
|
||||
m_isRunning = false;
|
||||
setDeletable( true );
|
||||
QMessageBox::critical( RiaGuiApplication::widgetToUseAsParent(),
|
||||
name(),
|
||||
"Failed to start job. Check log window for additional information." );
|
||||
}
|
||||
RimProcessQueue::queueProcess( m_process );
|
||||
onProgress( m_percentageDone );
|
||||
|
||||
return startOk;
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -184,9 +186,7 @@ bool RimGenericJob::execute()
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimGenericJob::setFinished( bool runOk )
|
||||
{
|
||||
m_isRunning = false;
|
||||
|
||||
m_lastRunFailed = !runOk;
|
||||
m_jobState = runOk ? JobState::Completed : JobState::Failed;
|
||||
|
||||
m_percentageDone = 100.0;
|
||||
onProgress( m_percentageDone );
|
||||
@@ -197,6 +197,15 @@ bool RimGenericJob::setFinished( bool runOk )
|
||||
return runOk;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimGenericJob::setStarted()
|
||||
{
|
||||
m_jobState = RimGenericJob::JobState::Running;
|
||||
onProgress( m_percentageDone );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -206,22 +215,32 @@ void RimGenericJob::defineObjectEditorAttribute( QString uiConfigName, caf::PdmU
|
||||
static auto contrastWarnColor =
|
||||
QColor( RiaColorTools::toQColor( RiaColorTools::contrastColor( cvf::Color3f( cvf::Color3f::DARK_YELLOW ) ) ) );
|
||||
|
||||
static auto waitColor = QColor( RiaColorTools::toQColor( cvf::Color3f( cvf::Color3f::LIGHT_GRAY ) ) );
|
||||
static auto contrastWaitColor =
|
||||
QColor( RiaColorTools::toQColor( RiaColorTools::contrastColor( cvf::Color3f( cvf::Color3f::LIGHT_GRAY ) ) ) );
|
||||
|
||||
if ( auto* treeItemAttribute = dynamic_cast<caf::PdmUiTreeViewItemAttribute*>( attribute ) )
|
||||
{
|
||||
if ( m_lastRunFailed )
|
||||
if ( m_jobState == JobState::Failed )
|
||||
{
|
||||
auto txt = m_errorsDetected > 0 ? QString( "[%1]" ).arg( m_errorsDetected ) : "!!!";
|
||||
auto tag =
|
||||
caf::PdmUiTreeViewItemAttribute::createTag( QColor( Qt::red ), RiuGuiTheme::getColorByVariableName( "backgroundColor1" ), txt );
|
||||
treeItemAttribute->tags.push_back( std::move( tag ) );
|
||||
}
|
||||
else
|
||||
else if ( m_jobState == JobState::Queued )
|
||||
{
|
||||
auto tag = caf::PdmUiTreeViewItemAttribute::createTag();
|
||||
tag->text = "Waiting...";
|
||||
tag->bgColor = waitColor;
|
||||
tag->fgColor = contrastWaitColor;
|
||||
treeItemAttribute->tags.push_back( std::move( tag ) );
|
||||
}
|
||||
else if ( ( m_jobState == JobState::Running ) || ( m_jobState == JobState::Completed ) )
|
||||
{
|
||||
if ( ( m_percentageDone == 0.0 ) && ( !m_isRunning ) ) return;
|
||||
|
||||
auto tag = caf::PdmUiTreeViewItemAttribute::createTag();
|
||||
|
||||
if ( m_isRunning )
|
||||
if ( m_jobState == JobState::Running )
|
||||
{
|
||||
tag->text = QString( "%1 %" ).arg( m_percentageDone, 0, 'f', 1 );
|
||||
}
|
||||
@@ -258,7 +277,6 @@ void RimGenericJob::defineObjectEditorAttribute( QString uiConfigName, caf::PdmU
|
||||
const QStringList RimGenericJob::jobLog() const
|
||||
{
|
||||
if ( m_process.isNull() ) return QStringList();
|
||||
|
||||
return m_process->stdOut();
|
||||
}
|
||||
|
||||
|
||||
@@ -38,15 +38,27 @@ class RimGenericJob : public RimNamedObject
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
enum JobState
|
||||
{
|
||||
Idle, // default, nothing is happening
|
||||
Queued, // waiting for available resources to run
|
||||
Running, // job is running
|
||||
Completed, // job completed without errors
|
||||
Failed // job completed with errors
|
||||
};
|
||||
|
||||
RimGenericJob();
|
||||
~RimGenericJob() override;
|
||||
|
||||
bool execute();
|
||||
bool setFinished( bool runOk );
|
||||
void setStarted();
|
||||
|
||||
bool isRunning() const;
|
||||
bool stop();
|
||||
|
||||
JobState state() const;
|
||||
|
||||
double percentageDone() const;
|
||||
const QStringList jobLog() const;
|
||||
|
||||
@@ -72,7 +84,6 @@ protected:
|
||||
int m_errorsDetected;
|
||||
|
||||
private:
|
||||
bool m_lastRunFailed;
|
||||
bool m_isRunning;
|
||||
JobState m_jobState;
|
||||
caf::PdmPointer<RimProcess> m_process;
|
||||
};
|
||||
|
||||
@@ -128,4 +128,10 @@ void RimJobCollection::deleteAllJobs()
|
||||
void RimJobCollection::appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const
|
||||
{
|
||||
menuBuilder << "RicNewOpmFlowJobFeature";
|
||||
|
||||
if ( numberOfRunningJobs() > 0 )
|
||||
{
|
||||
menuBuilder.addSeparator();
|
||||
menuBuilder << "RicStopAllJobsFeature";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,3 +68,16 @@ void RimJobMonitor::finished( int exitCode, QProcess::ExitStatus exitStatus )
|
||||
|
||||
RimProcessMonitor::finished( exitCode, exitStatus );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimJobMonitor::started()
|
||||
{
|
||||
if ( m_job.notNull() )
|
||||
{
|
||||
m_job->setStarted();
|
||||
}
|
||||
|
||||
RimProcessMonitor::started();
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ public:
|
||||
protected:
|
||||
void readyReadStandardOutput() override;
|
||||
void finished( int exitCode, QProcess::ExitStatus exitStatus ) override;
|
||||
void started() override;
|
||||
|
||||
private:
|
||||
caf::PdmPointer<RimGenericJob> m_job;
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
set(SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RimProcess.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimProcessMonitor.h
|
||||
set(SOURCE_GROUP_HEADER_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimProcess.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimProcessMonitor.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimProcessQueue.h
|
||||
)
|
||||
|
||||
set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RimProcess.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimProcessMonitor.cpp
|
||||
set(SOURCE_GROUP_SOURCE_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimProcess.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimProcessMonitor.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimProcessQueue.cpp
|
||||
)
|
||||
|
||||
list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimProcess, "RimProcess" );
|
||||
|
||||
int RimProcess::m_nextProcessId = 1;
|
||||
size_t RimProcess::m_nextProcessId = 1;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
@@ -38,7 +38,7 @@ RimProcess::RimProcess( bool logStdOutErr /*true*/, RimProcessMonitor* monitor )
|
||||
: m_enableLogging( logStdOutErr )
|
||||
, m_qProcess( nullptr )
|
||||
{
|
||||
int defId = m_nextProcessId++;
|
||||
size_t defId = m_nextProcessId++;
|
||||
if ( monitor == nullptr )
|
||||
m_monitor = new RimProcessMonitor( defId, logStdOutErr );
|
||||
else
|
||||
@@ -160,9 +160,9 @@ QStringList RimProcess::stdErr() const
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
int RimProcess::ID() const
|
||||
size_t RimProcess::ID() const
|
||||
{
|
||||
return m_id;
|
||||
return m_id();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -197,9 +197,9 @@ bool RimProcess::start( bool enableStdOut, bool enableStdErr )
|
||||
}
|
||||
|
||||
m_qProcess->start( m_command, m_arguments );
|
||||
auto error = m_qProcess->errorString();
|
||||
if ( !m_qProcess->waitForStarted( -1 ) )
|
||||
{
|
||||
auto error = m_qProcess->errorString();
|
||||
RiaLogging::error( QString( "Failed to start process %1. %2." ).arg( m_id() ).arg( error ) );
|
||||
return false;
|
||||
}
|
||||
@@ -233,6 +233,14 @@ void RimProcess::terminate()
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimProcess::notifyErrorFinish()
|
||||
{
|
||||
if ( m_monitor ) m_monitor->finished( -1, QProcess::CrashExit );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "cafPdmField.h"
|
||||
#include "cafPdmObject.h"
|
||||
|
||||
#include <QPointer>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
@@ -51,7 +52,7 @@ public:
|
||||
|
||||
QString command() const;
|
||||
QStringList parameters() const;
|
||||
int ID() const;
|
||||
size_t ID() const;
|
||||
|
||||
// blocking run
|
||||
bool execute( bool enableStdOut = true, bool enableStdErr = true );
|
||||
@@ -60,6 +61,7 @@ public:
|
||||
bool start( bool enableStdOut = true, bool enableStdErr = true );
|
||||
void cleanUpAfterRun();
|
||||
void terminate();
|
||||
void notifyErrorFinish();
|
||||
|
||||
QStringList stdErr() const;
|
||||
QStringList stdOut() const;
|
||||
@@ -77,13 +79,13 @@ private:
|
||||
caf::PdmField<QString> m_command;
|
||||
QStringList m_arguments;
|
||||
caf::PdmField<QString> m_description;
|
||||
caf::PdmField<int> m_id;
|
||||
caf::PdmField<size_t> m_id;
|
||||
caf::PdmField<caf::FilePath> m_workDir;
|
||||
|
||||
std::vector<std::pair<QString, QString>> m_environmentVariables;
|
||||
|
||||
static int m_nextProcessId;
|
||||
static size_t m_nextProcessId;
|
||||
RimProcessMonitor* m_monitor;
|
||||
bool m_enableLogging;
|
||||
QProcess* m_qProcess;
|
||||
QPointer<QProcess> m_qProcess;
|
||||
};
|
||||
|
||||
@@ -20,13 +20,15 @@
|
||||
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include "RimProcessQueue.h"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QtCore/QtCore>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimProcessMonitor::RimProcessMonitor( int processId, bool logStdOutErr /*true*/ )
|
||||
RimProcessMonitor::RimProcessMonitor( size_t processId, bool logStdOutErr /*true*/ )
|
||||
: QObject( nullptr )
|
||||
, m_processId( processId )
|
||||
, m_logStdOutErr( logStdOutErr )
|
||||
@@ -79,20 +81,23 @@ void RimProcessMonitor::error( QProcess::ProcessError error )
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimProcessMonitor::finished( int exitCode, QProcess::ExitStatus exitStatus )
|
||||
{
|
||||
if ( !m_logStdOutErr ) return;
|
||||
|
||||
QString finishStr;
|
||||
switch ( exitStatus )
|
||||
if ( m_logStdOutErr )
|
||||
{
|
||||
case QProcess::NormalExit:
|
||||
finishStr = QString( "Normal exit, code %1" ).arg( exitCode );
|
||||
break;
|
||||
case QProcess::CrashExit:
|
||||
default:
|
||||
finishStr = QString( "Crash exit, code %1" ).arg( exitCode );
|
||||
break;
|
||||
QString finishStr;
|
||||
switch ( exitStatus )
|
||||
{
|
||||
case QProcess::NormalExit:
|
||||
finishStr = QString( "Normal exit, code %1" ).arg( exitCode );
|
||||
break;
|
||||
case QProcess::CrashExit:
|
||||
default:
|
||||
finishStr = QString( "Crash exit, code %1" ).arg( exitCode );
|
||||
break;
|
||||
}
|
||||
RiaLogging::debug( addPrefix( finishStr ) );
|
||||
}
|
||||
RiaLogging::debug( addPrefix( finishStr ) );
|
||||
|
||||
RimProcessQueue::onProcessFinished( m_processId );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -175,7 +180,7 @@ QStringList RimProcessMonitor::stdErr() const
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimProcessMonitor::setProcessId( int processId )
|
||||
void RimProcessMonitor::setProcessId( size_t processId )
|
||||
{
|
||||
m_processId = processId;
|
||||
}
|
||||
|
||||
@@ -28,13 +28,13 @@ class RimProcessMonitor : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RimProcessMonitor( int processId, bool logStdOutErr = true );
|
||||
explicit RimProcessMonitor( size_t processId, bool logStdOutErr = true );
|
||||
|
||||
void clearStdOutErr();
|
||||
QStringList stdOut() const;
|
||||
QStringList stdErr() const;
|
||||
|
||||
void setProcessId( int processId );
|
||||
void setProcessId( size_t processId );
|
||||
|
||||
signals:
|
||||
|
||||
@@ -47,7 +47,7 @@ public slots:
|
||||
|
||||
protected:
|
||||
QString addPrefix( QString message );
|
||||
int m_processId;
|
||||
size_t m_processId;
|
||||
bool m_logStdOutErr;
|
||||
QStringList m_stdOut;
|
||||
QStringList m_stdErr;
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2026 Equinor ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight 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 General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RimProcessQueue.h"
|
||||
|
||||
#include "RimProcess.h"
|
||||
|
||||
#include "RiaPreferencesOpm.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimProcessQueue::RimProcessQueue()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimProcessQueue* RimProcessQueue::instance()
|
||||
{
|
||||
static RimProcessQueue theInstance;
|
||||
return &theInstance;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RimProcessQueue::queueProcess( RimProcess* process )
|
||||
{
|
||||
if ( process == nullptr ) return 0;
|
||||
return instance()->internalQueueProcess( process );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimProcessQueue::stopProcess( size_t processId )
|
||||
{
|
||||
instance()->internalStopProcess( processId );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimProcessQueue::onProcessFinished( size_t processId )
|
||||
{
|
||||
instance()->internalOnProcessFinished( processId );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimProcessQueue::launchNextProcessIfPossible()
|
||||
{
|
||||
if ( m_waitingProcesses.empty() ) return;
|
||||
|
||||
if ( m_runningProcesses.size() >= RiaPreferencesOpm::current()->maxParallelJobs() ) return;
|
||||
|
||||
auto nextProcess = m_waitingProcesses.front();
|
||||
m_waitingProcesses.pop_front();
|
||||
if ( nextProcess->start() )
|
||||
{
|
||||
m_runningProcesses.push_back( nextProcess );
|
||||
}
|
||||
else
|
||||
{
|
||||
nextProcess->notifyErrorFinish();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RimProcessQueue::internalQueueProcess( RimProcess* process )
|
||||
{
|
||||
QMutexLocker locker( &m_mutex );
|
||||
|
||||
m_waitingProcesses.push_back( process );
|
||||
size_t processId = process->ID();
|
||||
launchNextProcessIfPossible();
|
||||
return processId;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimProcessQueue::internalStopProcess( size_t processId )
|
||||
{
|
||||
bool isWaitingProcess = false;
|
||||
RimProcess* theProcess = nullptr;
|
||||
|
||||
{
|
||||
QMutexLocker locker( &m_mutex );
|
||||
|
||||
for ( auto proc : m_waitingProcesses )
|
||||
{
|
||||
if ( proc->ID() == processId )
|
||||
{
|
||||
m_waitingProcesses.remove( proc );
|
||||
theProcess = proc;
|
||||
isWaitingProcess = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !isWaitingProcess )
|
||||
{
|
||||
for ( auto proc : m_runningProcesses )
|
||||
{
|
||||
if ( proc->ID() == processId )
|
||||
{
|
||||
theProcess = proc;
|
||||
m_runningProcesses.remove( proc );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
launchNextProcessIfPossible();
|
||||
}
|
||||
|
||||
if ( theProcess != nullptr )
|
||||
{
|
||||
if ( isWaitingProcess )
|
||||
{
|
||||
theProcess->notifyErrorFinish();
|
||||
}
|
||||
else
|
||||
{
|
||||
theProcess->terminate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimProcessQueue::internalOnProcessFinished( size_t processId )
|
||||
{
|
||||
QMutexLocker locker( &m_mutex );
|
||||
|
||||
bool isWaitingProcess = false;
|
||||
|
||||
for ( auto proc : m_waitingProcesses )
|
||||
{
|
||||
if ( proc->ID() == processId )
|
||||
{
|
||||
m_waitingProcesses.remove( proc );
|
||||
isWaitingProcess = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !isWaitingProcess )
|
||||
{
|
||||
for ( auto proc : m_runningProcesses )
|
||||
{
|
||||
if ( proc->ID() == processId )
|
||||
{
|
||||
m_runningProcesses.remove( proc );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
launchNextProcessIfPossible();
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2026 Equinor ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight 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 General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
|
||||
#include <QMutex>
|
||||
|
||||
class RimProcess;
|
||||
|
||||
class RimProcessQueue
|
||||
{
|
||||
public:
|
||||
static size_t queueProcess( RimProcess* process );
|
||||
static void stopProcess( size_t processId );
|
||||
static void onProcessFinished( size_t processId );
|
||||
|
||||
protected:
|
||||
RimProcessQueue();
|
||||
static RimProcessQueue* instance();
|
||||
|
||||
size_t internalQueueProcess( RimProcess* process );
|
||||
void internalOnProcessFinished( size_t processId );
|
||||
void internalStopProcess( size_t processId );
|
||||
|
||||
private:
|
||||
void launchNextProcessIfPossible();
|
||||
|
||||
std::list<RimProcess*> m_waitingProcesses;
|
||||
std::list<RimProcess*> m_runningProcesses;
|
||||
|
||||
QMutex m_mutex;
|
||||
};
|
||||
Reference in New Issue
Block a user