mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-26 07:00:17 -05:00
Add preferences for HPC/Batch scheduler Add slurm and LSF support Add batch job monitor Support launcher specific user settings Use internal timer, not global view timer Add some more options to batch monitor Improve naming Add tests for job lists
447 lines
15 KiB
C++
447 lines
15 KiB
C++
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2021 - 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 "RimProcess.h"
|
|
|
|
#include "RiaLogging.h"
|
|
#include "RiaPreferencesOpm.h"
|
|
#include "RiaQStringFormatter.h"
|
|
#include "RiaWslTools.h"
|
|
#include "RimProcessMonitor.h"
|
|
|
|
#include "cafPdmFieldCapability.h"
|
|
|
|
#include <QApplication>
|
|
#include <QProcess>
|
|
#include <QProcessEnvironment>
|
|
|
|
CAF_PDM_SOURCE_INIT( RimProcess, "RimProcess" );
|
|
|
|
size_t RimProcess::m_nextProcessId = 1;
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimProcess::RimProcess( bool logStdOutErr, std::shared_ptr<RimProcessMonitor> monitor )
|
|
: m_enableLogging( logStdOutErr )
|
|
, m_qProcess( nullptr )
|
|
{
|
|
size_t defId = m_nextProcessId++;
|
|
if ( monitor == nullptr )
|
|
m_monitor = std::make_shared<RimProcessMonitor>( defId, logStdOutErr );
|
|
else
|
|
{
|
|
m_monitor = monitor;
|
|
m_monitor->setProcessId( defId );
|
|
}
|
|
|
|
CAF_PDM_InitObject( "ResInsight Process", ":/Erase.png" );
|
|
|
|
CAF_PDM_InitFieldNoDefault( &m_command, "Command", "Command" );
|
|
m_command.uiCapability()->setUiReadOnly( true );
|
|
|
|
CAF_PDM_InitField( &m_workDir, "WorkDir", caf::FilePath(), "Working Directory" );
|
|
m_command.uiCapability()->setUiReadOnly( true );
|
|
|
|
CAF_PDM_InitFieldNoDefault( &m_description, "Description", "Description" );
|
|
m_description.uiCapability()->setUiReadOnly( true );
|
|
|
|
CAF_PDM_InitField( &m_id, "ID", defId, "ID" );
|
|
m_id.uiCapability()->setUiReadOnly( true );
|
|
|
|
CAF_PDM_InitField( &m_useWsl, "UseWSL", false, "Use WSL to launch process" );
|
|
m_useWsl.uiCapability()->setUiReadOnly( true );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimProcess::~RimProcess()
|
|
{
|
|
}
|
|
|
|
caf::PdmFieldHandle* RimProcess::userDescriptionField()
|
|
{
|
|
return &m_description;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimProcess::addParameter( QString paramStr )
|
|
{
|
|
m_arguments << paramStr.trimmed();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimProcess::addParameters( QStringList parameterList )
|
|
{
|
|
for ( int i = 0; i < parameterList.size(); i++ )
|
|
{
|
|
addParameter( parameterList[i] );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimProcess::setCommand( QString cmdStr )
|
|
{
|
|
m_command = cmdStr.trimmed();
|
|
|
|
QString shell = optionalCommandInterpreter();
|
|
if ( shell.isEmpty() ) return;
|
|
|
|
QString preParam = optionalPreParameters();
|
|
if ( !preParam.isEmpty() ) m_arguments.append( preParam );
|
|
|
|
m_arguments.append( cmdStr.trimmed() );
|
|
m_command = shell;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimProcess::setDescription( QString desc )
|
|
{
|
|
m_description = desc;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimProcess::description() const
|
|
{
|
|
return m_description();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::shared_ptr<RimProcessMonitor> RimProcess::monitor() const
|
|
{
|
|
return m_monitor;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimProcess::command() const
|
|
{
|
|
return m_command();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QStringList RimProcess::parameters() const
|
|
{
|
|
return m_arguments;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
const QStringList& RimProcess::stdOut() const
|
|
{
|
|
if ( m_monitor ) return m_monitor->stdOut();
|
|
return m_emptyList;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
const QStringList& RimProcess::stdErr() const
|
|
{
|
|
if ( m_monitor ) return m_monitor->stdErr();
|
|
return m_emptyList;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimProcess::appendStdOut( QString line )
|
|
{
|
|
if ( m_monitor ) m_monitor->appendStdOut( line );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimProcess::appendStdErr( QString line )
|
|
{
|
|
if ( m_monitor ) m_monitor->appendStdErr( line );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
size_t RimProcess::ID() const
|
|
{
|
|
return m_id();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimProcess::start( bool enableStdOut, bool enableStdErr )
|
|
{
|
|
if ( !m_monitor ) return false;
|
|
if ( m_qProcess != nullptr ) return false;
|
|
|
|
m_qProcess = new QProcess();
|
|
QString cmd = commandLine();
|
|
|
|
if ( m_enableLogging )
|
|
{
|
|
std::string wslTxt = m_useWsl() ? " using WSL" : "";
|
|
RiaLogging::info( std::format( "Start process {}{}: {}", m_id(), wslTxt, cmd ) );
|
|
}
|
|
|
|
m_monitor->clearStdOutErr();
|
|
|
|
QObject::connect( m_qProcess, SIGNAL( finished( int, QProcess::ExitStatus ) ), m_monitor.get(), SLOT( finished( int, QProcess::ExitStatus ) ) );
|
|
if ( enableStdOut )
|
|
QObject::connect( m_qProcess, SIGNAL( readyReadStandardOutput() ), m_monitor.get(), SLOT( readyReadStandardOutput() ) );
|
|
if ( enableStdErr )
|
|
QObject::connect( m_qProcess, SIGNAL( readyReadStandardError() ), m_monitor.get(), SLOT( readyReadStandardError() ) );
|
|
QObject::connect( m_qProcess, SIGNAL( started() ), m_monitor.get(), SLOT( started() ) );
|
|
|
|
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
|
for ( auto& [key, val] : m_environmentVariables )
|
|
{
|
|
env.insert( key, val );
|
|
}
|
|
m_qProcess->setProcessEnvironment( env );
|
|
if ( !m_workDir().path().isEmpty() )
|
|
{
|
|
m_qProcess->setWorkingDirectory( m_workDir().path() );
|
|
}
|
|
|
|
if ( m_useWsl() )
|
|
{
|
|
m_arguments.prepend( m_command() );
|
|
m_arguments = RiaPreferencesOpm::current()->wslOptions() + m_arguments;
|
|
m_command = RiaWslTools::wslCommand();
|
|
}
|
|
|
|
m_qProcess->start( m_command, m_arguments );
|
|
if ( !m_qProcess->waitForStarted( -1 ) )
|
|
{
|
|
if ( m_enableLogging )
|
|
{
|
|
auto error = m_qProcess->errorString();
|
|
RiaLogging::error( std::format( "Failed to start process {}. {}.", m_id(), error ) );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimProcess::writeToStdIn( QStringList text )
|
|
{
|
|
if ( m_qProcess == nullptr ) return;
|
|
|
|
for ( auto& line : text )
|
|
{
|
|
m_qProcess->write( line.toUtf8() + "\n" );
|
|
m_qProcess->waitForBytesWritten();
|
|
m_qProcess->closeWriteChannel();
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimProcess::cleanUpAfterRun()
|
|
{
|
|
if ( m_qProcess != nullptr )
|
|
{
|
|
m_qProcess->deleteLater();
|
|
m_qProcess = nullptr;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimProcess::terminate()
|
|
{
|
|
if ( m_qProcess != nullptr )
|
|
{
|
|
if ( m_qProcess->state() == QProcess::Running )
|
|
{
|
|
m_qProcess->kill();
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimProcess::notifyErrorFinish()
|
|
{
|
|
if ( m_monitor ) m_monitor->finished( -1, QProcess::CrashExit );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimProcess::execute( bool enableStdOut, bool enableStdErr )
|
|
{
|
|
if ( !start( enableStdOut, enableStdErr ) ) return false;
|
|
|
|
while ( !m_qProcess->waitForFinished( 250 ) )
|
|
{
|
|
QApplication::processEvents( QEventLoop::ExcludeUserInputEvents );
|
|
}
|
|
|
|
bool retval = ( m_qProcess->exitCode() == 0 );
|
|
|
|
cleanUpAfterRun();
|
|
|
|
return retval;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimProcess::optionalCommandInterpreter() const
|
|
{
|
|
if ( m_command.value().isNull() ) return "";
|
|
|
|
if ( isWindowsBatchFile() )
|
|
{
|
|
return "cmd.exe";
|
|
}
|
|
if ( m_command.value().endsWith( ".sh", Qt::CaseInsensitive ) )
|
|
{
|
|
return "bash";
|
|
}
|
|
if ( m_command.value().endsWith( ".csh", Qt::CaseInsensitive ) )
|
|
{
|
|
return "csh";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimProcess::optionalPreParameters() const
|
|
{
|
|
if ( m_command.value().isNull() ) return "";
|
|
|
|
if ( isWindowsBatchFile() )
|
|
{
|
|
return "/c";
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimProcess::isWindowsBatchFile() const
|
|
{
|
|
return ( m_command.value().endsWith( ".cmd", Qt::CaseInsensitive ) || m_command.value().endsWith( ".bat", Qt::CaseInsensitive ) );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimProcess::commandLine() const
|
|
{
|
|
QString cmdline = handleSpaces( m_command );
|
|
|
|
for ( int i = 0; i < m_arguments.size(); i++ )
|
|
{
|
|
cmdline += " ";
|
|
cmdline += handleSpaces( m_arguments[i] );
|
|
}
|
|
|
|
return cmdline;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimProcess::handleSpaces( QString arg ) const
|
|
{
|
|
if ( arg.contains( " " ) && !arg.startsWith( "\"" ) )
|
|
{
|
|
return QString( "\"" + arg + "\"" );
|
|
}
|
|
return arg;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimProcess::addEnvironmentVariable( QString name, QString value )
|
|
{
|
|
m_environmentVariables.push_back( std::make_pair( name, value ) );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
const std::vector<std::pair<QString, QString>>& RimProcess::environmentVariables() const
|
|
{
|
|
return m_environmentVariables;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimProcess::setWorkingDirectory( QString workDir )
|
|
{
|
|
m_workDir = workDir;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimProcess::workingDirectory() const
|
|
{
|
|
return m_workDir().path();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimProcess::useWsl() const
|
|
{
|
|
return m_useWsl();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimProcess::setUseWsl( bool useWsl )
|
|
{
|
|
m_useWsl = useWsl;
|
|
}
|