mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-25 06:38:13 -05:00
Workflows: Add UI for taskmaestro workflows
Discover taskmaestro workflows under ~/.taskmaestro/workflows/ and surface them as a "Workflows" collection in the project tree alongside Scripts. Each workflow's task input schema is auto-generated by introspecting the Pydantic models via a Python helper (rips.taskmaestro_helper) and rendered as PDM field bindings - scalars for str/int/float/bool, and PdmPtrField pickers for fields typed as rips.Case / rips.WellPath / rips.View. The Run button on a RimWorkflow writes input.yaml from current bindings (rips object references serialized as __resinsight_ref__ maps), spawns the helper's run subcommand, and streams output to a modeless log dialog. The helper resolves __resinsight_ref__ markers back to live rips objects via the running ResInsight's gRPC server before driving taskmaestro.
This commit is contained in:
1 parent
63eaa1266d
commit
3a7b8f6be8
49 files changed
+2861
No files matched your search
@@ -128,6 +128,7 @@ list(
|
||||
ProjectDataModel/ProcessControl/CMakeLists_files.cmake
|
||||
ProjectDataModel/Polygons/CMakeLists_files.cmake
|
||||
ProjectDataModel/Polygons/Cloud/CMakeLists_files.cmake
|
||||
ProjectDataModel/Workflow/CMakeLists_files.cmake
|
||||
ProjectDataModel/WellLog/CMakeLists_files.cmake
|
||||
ProjectDataModel/WellLog/WellLogTrack/CMakeLists_files.cmake
|
||||
ProjectDataModel/WellMeasurement/CMakeLists_files.cmake
|
||||
|
||||
@@ -60,6 +60,7 @@ set(COMMAND_REFERENCED_CMAKE_FILES
|
||||
FractureCommands/CMakeLists_files.cmake
|
||||
PlotBuilderCommands/CMakeLists_files.cmake
|
||||
PolygonCommands/CMakeLists_files.cmake
|
||||
WorkflowCommands/CMakeLists_files.cmake
|
||||
Sumo/CMakeLists_files.cmake
|
||||
ToolCommands/CMakeLists_files.cmake
|
||||
3dView/CMakeLists_files.cmake
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
set(SOURCE_GROUP_HEADER_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicNewWorkflowJobFeature.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicRunWorkflowJobFeature.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicCancelWorkflowJobFeature.h
|
||||
)
|
||||
|
||||
set(SOURCE_GROUP_SOURCE_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicNewWorkflowJobFeature.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicRunWorkflowJobFeature.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicCancelWorkflowJobFeature.cpp
|
||||
)
|
||||
|
||||
list(APPEND COMMAND_CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})
|
||||
list(APPEND COMMAND_CODE_SOURCE_FILES ${SOURCE_GROUP_SOURCE_FILES})
|
||||
@@ -0,0 +1,46 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RicCancelWorkflowJobFeature.h"
|
||||
|
||||
#include "Workflow/RimWorkflowJob.h"
|
||||
|
||||
#include "cafSelectionManagerTools.h"
|
||||
|
||||
#include <QAction>
|
||||
|
||||
CAF_CMD_SOURCE_INIT( RicCancelWorkflowJobFeature, "RicCancelWorkflowJobFeature" );
|
||||
|
||||
void RicCancelWorkflowJobFeature::onActionTriggered( bool isChecked )
|
||||
{
|
||||
auto jobs = caf::selectedObjectsByType<RimWorkflowJob*>();
|
||||
if ( jobs.size() != 1 ) return;
|
||||
jobs.front()->cancelJob();
|
||||
}
|
||||
|
||||
void RicCancelWorkflowJobFeature::setupActionLook( QAction* actionToSetup )
|
||||
{
|
||||
actionToSetup->setText( "Cancel Job" );
|
||||
actionToSetup->setIcon( QIcon( ":/stop.svg" ) );
|
||||
}
|
||||
|
||||
bool RicCancelWorkflowJobFeature::isCommandEnabled() const
|
||||
{
|
||||
auto jobs = caf::selectedObjectsByType<RimWorkflowJob*>();
|
||||
return jobs.size() == 1 && jobs.front()->isRunning();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 RicCancelWorkflowJobFeature : public caf::CmdFeature
|
||||
{
|
||||
CAF_CMD_HEADER_INIT;
|
||||
|
||||
protected:
|
||||
void onActionTriggered( bool isChecked ) override;
|
||||
void setupActionLook( QAction* actionToSetup ) override;
|
||||
bool isCommandEnabled() const override;
|
||||
};
|
||||
@@ -0,0 +1,65 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RicNewWorkflowJobFeature.h"
|
||||
|
||||
#include "Workflow/RimWorkflow.h"
|
||||
#include "Workflow/RimWorkflowJob.h"
|
||||
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include "cafPdmDefaultObjectFactory.h"
|
||||
#include "cafSelectionManagerTools.h"
|
||||
|
||||
#include <QAction>
|
||||
|
||||
CAF_CMD_SOURCE_INIT( RicNewWorkflowJobFeature, "RicNewWorkflowJobFeature" );
|
||||
|
||||
void RicNewWorkflowJobFeature::onActionTriggered( bool isChecked )
|
||||
{
|
||||
auto workflows = caf::selectedObjectsByType<RimWorkflow*>();
|
||||
if ( workflows.size() != 1 ) return;
|
||||
|
||||
RimWorkflow* workflow = workflows.front();
|
||||
auto jobs = workflow->jobs();
|
||||
if ( jobs.empty() )
|
||||
{
|
||||
RiaLogging::warning( "Cannot create a new job: workflow has no template job to clone." );
|
||||
return;
|
||||
}
|
||||
|
||||
auto* clone =
|
||||
dynamic_cast<RimWorkflowJob*>( jobs.front()->xmlCapability()->copyByXmlSerialization( caf::PdmDefaultObjectFactory::instance() ) );
|
||||
if ( !clone ) return;
|
||||
|
||||
clone->setJobName( QString( "Job %1" ).arg( jobs.size() + 1 ) );
|
||||
workflow->addJob( clone );
|
||||
clone->resolveReferencesRecursively();
|
||||
workflow->uiCapability()->updateAllRequiredEditors();
|
||||
}
|
||||
|
||||
void RicNewWorkflowJobFeature::setupActionLook( QAction* actionToSetup )
|
||||
{
|
||||
actionToSetup->setText( "New Job" );
|
||||
actionToSetup->setIcon( QIcon( ":/caf/duplicate.svg" ) );
|
||||
}
|
||||
|
||||
bool RicNewWorkflowJobFeature::isCommandEnabled() const
|
||||
{
|
||||
return caf::selectedObjectsByType<RimWorkflow*>().size() == 1;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 RicNewWorkflowJobFeature : public caf::CmdFeature
|
||||
{
|
||||
CAF_CMD_HEADER_INIT;
|
||||
|
||||
protected:
|
||||
void onActionTriggered( bool isChecked ) override;
|
||||
void setupActionLook( QAction* actionToSetup ) override;
|
||||
bool isCommandEnabled() const override;
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RicRunWorkflowJobFeature.h"
|
||||
|
||||
#include "Workflow/RimWorkflowJob.h"
|
||||
|
||||
#include "cafSelectionManagerTools.h"
|
||||
|
||||
#include <QAction>
|
||||
|
||||
CAF_CMD_SOURCE_INIT( RicRunWorkflowJobFeature, "RicRunWorkflowJobFeature" );
|
||||
|
||||
void RicRunWorkflowJobFeature::onActionTriggered( bool isChecked )
|
||||
{
|
||||
auto jobs = caf::selectedObjectsByType<RimWorkflowJob*>();
|
||||
if ( jobs.size() != 1 ) return;
|
||||
jobs.front()->runJob();
|
||||
}
|
||||
|
||||
void RicRunWorkflowJobFeature::setupActionLook( QAction* actionToSetup )
|
||||
{
|
||||
actionToSetup->setText( "Run" );
|
||||
actionToSetup->setIcon( QIcon( ":/Play.svg" ) );
|
||||
}
|
||||
|
||||
bool RicRunWorkflowJobFeature::isCommandEnabled() const
|
||||
{
|
||||
auto jobs = caf::selectedObjectsByType<RimWorkflowJob*>();
|
||||
return jobs.size() == 1 && !jobs.front()->isRunning();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 RicRunWorkflowJobFeature : public caf::CmdFeature
|
||||
{
|
||||
CAF_CMD_HEADER_INIT;
|
||||
|
||||
protected:
|
||||
void onActionTriggered( bool isChecked ) override;
|
||||
void setupActionLook( QAction* actionToSetup ) override;
|
||||
bool isCommandEnabled() const override;
|
||||
};
|
||||
@@ -102,6 +102,7 @@
|
||||
#include "Tools/RimAutomationSettings.h"
|
||||
#include "VerticalFlowPerformance/RimVfpDataCollection.h"
|
||||
#include "VerticalFlowPerformance/RimVfpPlotCollection.h"
|
||||
#include "Workflow/RimWorkflowCollection.h"
|
||||
|
||||
#include "RiuPlotMainWindow.h"
|
||||
|
||||
@@ -137,6 +138,9 @@ RimProject::RimProject()
|
||||
CAF_PDM_InitFieldNoDefault( &scriptCollection, "ScriptCollection", "Octave Scripts", ":/octave.png" );
|
||||
scriptCollection.xmlCapability()->disableIO();
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &workflowCollection, "WorkflowCollection", "Workflows", ":/Folder.png" );
|
||||
workflowCollection.xmlCapability()->disableIO();
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_jobCollection, "JobCollection", "Jobs", ":/gear.png" );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_mainPlotCollection, "MainPlotCollection", "Plots" );
|
||||
@@ -196,6 +200,9 @@ RimProject::RimProject()
|
||||
scriptCollection->uiCapability()->setUiName( "Scripts" );
|
||||
scriptCollection->uiCapability()->setUiIconFromResourceString( ":/octave.png" );
|
||||
|
||||
workflowCollection = new RimWorkflowCollection();
|
||||
workflowCollection->uiCapability()->setUiName( "Workflows" );
|
||||
|
||||
m_mainPlotCollection = new RimMainPlotCollection();
|
||||
m_pinnedFieldCollection = new RimQuickAccessCollection();
|
||||
m_jobCollection = new RimJobCollection();
|
||||
@@ -1487,6 +1494,7 @@ void RimProject::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, Q
|
||||
{
|
||||
uiTreeOrdering.add( scriptCollection() );
|
||||
uiTreeOrdering.add( jobCollection() );
|
||||
uiTreeOrdering.add( workflowCollection() );
|
||||
}
|
||||
else if ( uiConfigName == "PlotWindow.Templates" )
|
||||
{
|
||||
|
||||
@@ -52,6 +52,7 @@ class RimObservedSummaryData;
|
||||
class RimOilField;
|
||||
class RimColorLegendCollection;
|
||||
class RimScriptCollection;
|
||||
class RimWorkflowCollection;
|
||||
class RimSummaryCase;
|
||||
class RimSummaryEnsemble;
|
||||
class RimSummaryCaseMainCollection;
|
||||
@@ -98,6 +99,7 @@ public:
|
||||
caf::PdmChildArrayField<RimOilField*> oilFields;
|
||||
caf::PdmChildField<RimColorLegendCollection*> colorLegendCollection;
|
||||
caf::PdmChildField<RimScriptCollection*> scriptCollection;
|
||||
caf::PdmChildField<RimWorkflowCollection*> workflowCollection;
|
||||
caf::PdmChildField<RimViewLinkerCollection*> viewLinkerCollection;
|
||||
caf::PdmChildField<RimSummaryCalculationCollection*> calculationCollection;
|
||||
caf::PdmChildField<RimGridCalculationCollection*> gridCalculationCollection;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
set(SOURCE_GROUP_HEADER_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflow.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowCollection.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowJob.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowTaskInput.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowFieldBinding.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowStringBinding.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowFloatBinding.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowIntBinding.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowBoolBinding.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowCaseBinding.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowWellPathBinding.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowViewBinding.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowDateBinding.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowFilePathBinding.h
|
||||
)
|
||||
|
||||
set(SOURCE_GROUP_SOURCE_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflow.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowCollection.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowJob.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowTaskInput.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowFieldBinding.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowStringBinding.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowFloatBinding.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowIntBinding.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowBoolBinding.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowCaseBinding.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowWellPathBinding.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowViewBinding.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowDateBinding.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWorkflowFilePathBinding.cpp
|
||||
)
|
||||
|
||||
list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})
|
||||
|
||||
list(APPEND CODE_SOURCE_FILES ${SOURCE_GROUP_SOURCE_FILES})
|
||||
|
||||
source_group(
|
||||
"ProjectDataModel\\Workflow"
|
||||
FILES ${SOURCE_GROUP_HEADER_FILES} ${SOURCE_GROUP_SOURCE_FILES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/CMakeLists_files.cmake
|
||||
)
|
||||
@@ -0,0 +1,204 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflow.h"
|
||||
|
||||
#include "RimWorkflowJob.h"
|
||||
#include "RimWorkflowTaskInput.h"
|
||||
|
||||
#include "RiaLogging.h"
|
||||
#include "RiaPreferences.h"
|
||||
|
||||
#include "cafCmdFeatureMenuBuilder.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QProcess>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QStandardPaths>
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimWorkflow, "Workflow" );
|
||||
|
||||
RimWorkflow::RimWorkflow()
|
||||
{
|
||||
CAF_PDM_InitObject( "Workflow", ":/Folder.png" );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_name, "Name", "Name" );
|
||||
m_name.uiCapability()->setUiReadOnly( true );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_description, "Description", "Description" );
|
||||
m_description.uiCapability()->setUiReadOnly( true );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_workflowDirectory, "WorkflowDirectory", "Directory" );
|
||||
m_workflowDirectory.uiCapability()->setUiReadOnly( true );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_loadError, "LoadError", "Load Error" );
|
||||
m_loadError.uiCapability()->setUiReadOnly( true );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_jobs, "Jobs", "" );
|
||||
}
|
||||
|
||||
QString RimWorkflow::name() const
|
||||
{
|
||||
return m_name();
|
||||
}
|
||||
|
||||
void RimWorkflow::setWorkflowDirectory( const QString& directory )
|
||||
{
|
||||
m_workflowDirectory = directory;
|
||||
QString name = QFileInfo( QDir( directory ).absolutePath() ).fileName();
|
||||
m_name = name;
|
||||
setUiName( name );
|
||||
}
|
||||
|
||||
QString RimWorkflow::workflowDirectory() const
|
||||
{
|
||||
return m_workflowDirectory().path();
|
||||
}
|
||||
|
||||
std::vector<RimWorkflowJob*> RimWorkflow::jobs() const
|
||||
{
|
||||
std::vector<RimWorkflowJob*> result;
|
||||
result.reserve( m_jobs.size() );
|
||||
for ( RimWorkflowJob* j : m_jobs.childrenByType() )
|
||||
{
|
||||
if ( j ) result.push_back( j );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void RimWorkflow::addJob( RimWorkflowJob* job )
|
||||
{
|
||||
if ( job ) m_jobs.push_back( job );
|
||||
}
|
||||
|
||||
void RimWorkflow::appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const
|
||||
{
|
||||
menuBuilder << "RicNewWorkflowJobFeature";
|
||||
}
|
||||
|
||||
bool RimWorkflow::loadFromDirectory( QString* errorMessage )
|
||||
{
|
||||
m_jobs.deleteChildren();
|
||||
m_loadError = "";
|
||||
|
||||
const QString dir = workflowDirectory();
|
||||
if ( dir.isEmpty() ) return false;
|
||||
|
||||
QString python = RimWorkflow::findPythonExecutable();
|
||||
if ( python.isEmpty() )
|
||||
{
|
||||
m_loadError = "No usable Python interpreter found";
|
||||
RiaLogging::warning( QString( "Workflow '%1': %2" ).arg( dir, m_loadError() ).toStdString() );
|
||||
if ( errorMessage ) *errorMessage = m_loadError;
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList args{ "-m", "rips.taskmaestro_helper", "introspect", dir };
|
||||
|
||||
QProcess proc;
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
proc.setProcessEnvironment( env );
|
||||
proc.start( python, args );
|
||||
if ( !proc.waitForStarted( 10000 ) )
|
||||
{
|
||||
m_loadError = QString( "Could not launch '%1'" ).arg( python );
|
||||
RiaLogging::warning( QString( "Workflow '%1': %2" ).arg( dir, m_loadError() ).toStdString() );
|
||||
if ( errorMessage ) *errorMessage = m_loadError;
|
||||
return false;
|
||||
}
|
||||
if ( !proc.waitForFinished( 30000 ) )
|
||||
{
|
||||
proc.kill();
|
||||
m_loadError = "Introspect helper timed out";
|
||||
RiaLogging::warning( QString( "Workflow '%1': %2" ).arg( dir, m_loadError() ).toStdString() );
|
||||
if ( errorMessage ) *errorMessage = m_loadError;
|
||||
return false;
|
||||
}
|
||||
|
||||
const QByteArray stdoutBytes = proc.readAllStandardOutput();
|
||||
if ( proc.exitStatus() != QProcess::NormalExit || proc.exitCode() != 0 )
|
||||
{
|
||||
m_loadError = QString::fromUtf8( proc.readAllStandardError() ).trimmed();
|
||||
if ( m_loadError().isEmpty() ) m_loadError = "Introspect helper failed";
|
||||
RiaLogging::warning( QString( "Workflow '%1' introspect failed: %2" ).arg( dir, m_loadError() ).toStdString() );
|
||||
if ( errorMessage ) *errorMessage = m_loadError;
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonParseError parseErr{};
|
||||
const QJsonDocument doc = QJsonDocument::fromJson( stdoutBytes, &parseErr );
|
||||
if ( parseErr.error != QJsonParseError::NoError || !doc.isObject() )
|
||||
{
|
||||
m_loadError = QString( "Invalid JSON from introspect: %1" ).arg( parseErr.errorString() );
|
||||
if ( errorMessage ) *errorMessage = m_loadError;
|
||||
return false;
|
||||
}
|
||||
|
||||
const QJsonObject root = doc.object();
|
||||
if ( root.contains( "name" ) ) m_name = root.value( "name" ).toString();
|
||||
if ( root.contains( "description" ) ) m_description = root.value( "description" ).toString();
|
||||
setUiName( m_name() );
|
||||
|
||||
std::vector<RimWorkflowTaskInput*> taskInputs;
|
||||
for ( const QJsonValue& tv : root.value( "tasks" ).toArray() )
|
||||
{
|
||||
const QJsonObject taskObj = tv.toObject();
|
||||
auto* input = new RimWorkflowTaskInput;
|
||||
input->setTaskName( taskObj.value( "name" ).toString() );
|
||||
input->buildFromSchema( taskObj.value( "config_fields" ).toArray() );
|
||||
taskInputs.push_back( input );
|
||||
}
|
||||
|
||||
auto* job = new RimWorkflowJob;
|
||||
job->setJobName( "Default" );
|
||||
job->setTaskInputs( taskInputs );
|
||||
m_jobs.push_back( job );
|
||||
|
||||
RiaLogging::info(
|
||||
QString( "Loaded workflow '%1' (%2 tasks with config) from %3" ).arg( m_name() ).arg( taskInputs.size() ).arg( dir ).toStdString() );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QString RimWorkflow::findPythonExecutable()
|
||||
{
|
||||
QStringList candidates;
|
||||
if ( auto* prefs = RiaPreferences::current() )
|
||||
{
|
||||
QString configured = prefs->pythonExecutable();
|
||||
if ( !configured.isEmpty() && configured != "python" ) candidates << configured;
|
||||
}
|
||||
candidates << "python3" << "python";
|
||||
|
||||
for ( const QString& cand : candidates )
|
||||
{
|
||||
if ( cand.contains( '/' ) || cand.contains( '\\' ) )
|
||||
{
|
||||
if ( QFileInfo( cand ).isExecutable() ) return cand;
|
||||
}
|
||||
else if ( !QStandardPaths::findExecutable( cand ).isEmpty() )
|
||||
{
|
||||
return cand;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "cafFilePath.h"
|
||||
#include "cafPdmChildArrayField.h"
|
||||
#include "cafPdmField.h"
|
||||
#include "cafPdmObject.h"
|
||||
|
||||
class RimWorkflowJob;
|
||||
|
||||
class RimWorkflow : public caf::PdmObject
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimWorkflow();
|
||||
|
||||
QString name() const;
|
||||
void setWorkflowDirectory( const QString& directory );
|
||||
QString workflowDirectory() const;
|
||||
|
||||
bool loadFromDirectory( QString* errorMessage = nullptr );
|
||||
|
||||
std::vector<RimWorkflowJob*> jobs() const;
|
||||
void addJob( RimWorkflowJob* job );
|
||||
|
||||
static QString findPythonExecutable();
|
||||
|
||||
protected:
|
||||
void appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const override;
|
||||
|
||||
private:
|
||||
caf::PdmField<QString> m_name;
|
||||
caf::PdmField<QString> m_description;
|
||||
caf::PdmField<caf::FilePath> m_workflowDirectory;
|
||||
caf::PdmField<QString> m_loadError;
|
||||
caf::PdmChildArrayField<RimWorkflowJob*> m_jobs;
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowBoolBinding.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimWorkflowBoolBinding, "WorkflowBoolBinding" );
|
||||
|
||||
RimWorkflowBoolBinding::RimWorkflowBoolBinding()
|
||||
{
|
||||
CAF_PDM_InitField( &m_value, "Value", false, "Value" );
|
||||
}
|
||||
|
||||
void RimWorkflowBoolBinding::applySchema( const QJsonObject& fieldSchema )
|
||||
{
|
||||
RimWorkflowFieldBinding::applySchema( fieldSchema );
|
||||
if ( fieldSchema.contains( "default" ) )
|
||||
{
|
||||
m_value = fieldSchema.value( "default" ).toBool( false );
|
||||
}
|
||||
}
|
||||
|
||||
QString RimWorkflowBoolBinding::toYamlValue() const
|
||||
{
|
||||
return m_value() ? "true" : "false";
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowFieldBinding.h"
|
||||
|
||||
class RimWorkflowBoolBinding : public RimWorkflowFieldBinding
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimWorkflowBoolBinding();
|
||||
|
||||
void applySchema( const QJsonObject& fieldSchema ) override;
|
||||
QString toYamlValue() const override;
|
||||
|
||||
caf::PdmFieldHandle* valueField() override { return &m_value; }
|
||||
|
||||
private:
|
||||
caf::PdmField<bool> m_value;
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowCaseBinding.h"
|
||||
|
||||
#include "RimEclipseCase.h"
|
||||
#include "RimTools.h"
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimWorkflowCaseBinding, "WorkflowCaseBinding" );
|
||||
|
||||
RimWorkflowCaseBinding::RimWorkflowCaseBinding()
|
||||
{
|
||||
CAF_PDM_InitFieldNoDefault( &m_case, "Case", "Case" );
|
||||
}
|
||||
|
||||
QString RimWorkflowCaseBinding::toYamlValue() const
|
||||
{
|
||||
if ( m_case() == nullptr ) return "null";
|
||||
return QString( "{__resinsight_ref__: EclipseCase, case_id: %1}" ).arg( m_case()->caseId() );
|
||||
}
|
||||
|
||||
QList<caf::PdmOptionItemInfo> RimWorkflowCaseBinding::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions )
|
||||
{
|
||||
QList<caf::PdmOptionItemInfo> options;
|
||||
if ( fieldNeedingOptions == &m_case ) RimTools::eclipseCaseOptionItems( &options );
|
||||
return options;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowFieldBinding.h"
|
||||
|
||||
#include "cafPdmPtrField.h"
|
||||
|
||||
class RimEclipseCase;
|
||||
|
||||
class RimWorkflowCaseBinding : public RimWorkflowFieldBinding
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimWorkflowCaseBinding();
|
||||
|
||||
QString toYamlValue() const override;
|
||||
|
||||
caf::PdmFieldHandle* valueField() override { return &m_case; }
|
||||
|
||||
private:
|
||||
QList<caf::PdmOptionItemInfo> calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override;
|
||||
|
||||
caf::PdmPtrField<RimEclipseCase*> m_case;
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowCollection.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimWorkflowCollection, "WorkflowCollection" );
|
||||
|
||||
RimWorkflowCollection::RimWorkflowCollection()
|
||||
{
|
||||
CAF_PDM_InitObject( "Workflows", ":/Folder.png" );
|
||||
CAF_PDM_InitFieldNoDefault( &m_items, "Workflows", "" );
|
||||
|
||||
rescanWorkflows();
|
||||
}
|
||||
|
||||
RimWorkflowCollection::~RimWorkflowCollection() = default;
|
||||
|
||||
QString RimWorkflowCollection::discoveryDirectory()
|
||||
{
|
||||
return QDir::homePath() + "/.taskmaestro/workflows";
|
||||
}
|
||||
|
||||
void RimWorkflowCollection::rescanWorkflows()
|
||||
{
|
||||
deleteAllItems();
|
||||
|
||||
QDir dir( discoveryDirectory() );
|
||||
if ( !dir.exists() ) return;
|
||||
|
||||
const QFileInfoList entries = dir.entryInfoList( QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name );
|
||||
for ( const QFileInfo& entry : entries )
|
||||
{
|
||||
if ( !QFileInfo( entry.absoluteFilePath() + "/workflow.yaml" ).isFile() ) continue;
|
||||
|
||||
auto* workflow = new RimWorkflow;
|
||||
workflow->setWorkflowDirectory( entry.absoluteFilePath() );
|
||||
workflow->loadFromDirectory();
|
||||
addItem( workflow );
|
||||
}
|
||||
}
|
||||
@@ -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 "RimWorkflow.h"
|
||||
|
||||
#include "cafPdmObjectCollection.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
class RimWorkflowCollection : public caf::PdmObjectCollection<RimWorkflow>
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimWorkflowCollection();
|
||||
~RimWorkflowCollection() override;
|
||||
|
||||
void rescanWorkflows();
|
||||
|
||||
static QString discoveryDirectory();
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowDateBinding.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimWorkflowDateBinding, "WorkflowDateBinding" );
|
||||
|
||||
RimWorkflowDateBinding::RimWorkflowDateBinding()
|
||||
{
|
||||
CAF_PDM_InitField( &m_value, "Value", QDate::currentDate(), "Value" );
|
||||
}
|
||||
|
||||
void RimWorkflowDateBinding::applySchema( const QJsonObject& fieldSchema )
|
||||
{
|
||||
RimWorkflowFieldBinding::applySchema( fieldSchema );
|
||||
if ( fieldSchema.contains( "default" ) )
|
||||
{
|
||||
QDate parsed = QDate::fromString( fieldSchema.value( "default" ).toString(), Qt::ISODate );
|
||||
if ( parsed.isValid() ) m_value = parsed;
|
||||
}
|
||||
}
|
||||
|
||||
QString RimWorkflowDateBinding::toYamlValue() const
|
||||
{
|
||||
return QString( "\"%1\"" ).arg( m_value().toString( Qt::ISODate ) );
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowFieldBinding.h"
|
||||
|
||||
#include <QDate>
|
||||
|
||||
class RimWorkflowDateBinding : public RimWorkflowFieldBinding
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimWorkflowDateBinding();
|
||||
|
||||
void applySchema( const QJsonObject& fieldSchema ) override;
|
||||
QString toYamlValue() const override;
|
||||
|
||||
caf::PdmFieldHandle* valueField() override { return &m_value; }
|
||||
|
||||
private:
|
||||
caf::PdmField<QDate> m_value;
|
||||
};
|
||||
@@ -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 "RimWorkflowFieldBinding.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
|
||||
CAF_PDM_ABSTRACT_SOURCE_INIT( RimWorkflowFieldBinding, "WorkflowFieldBinding" );
|
||||
|
||||
RimWorkflowFieldBinding::RimWorkflowFieldBinding()
|
||||
{
|
||||
CAF_PDM_InitObject( "Field", ":/Bullet.png" );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_fieldName, "FieldName", "Field" );
|
||||
m_fieldName.uiCapability()->setUiReadOnly( true );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_description, "Description", "Description" );
|
||||
m_description.uiCapability()->setUiReadOnly( true );
|
||||
|
||||
CAF_PDM_InitField( &m_required, "Required", false, "Required" );
|
||||
m_required.uiCapability()->setUiReadOnly( true );
|
||||
}
|
||||
|
||||
QString RimWorkflowFieldBinding::fieldName() const
|
||||
{
|
||||
return m_fieldName();
|
||||
}
|
||||
|
||||
void RimWorkflowFieldBinding::setFieldName( const QString& name )
|
||||
{
|
||||
m_fieldName = name;
|
||||
setUiName( name );
|
||||
}
|
||||
|
||||
void RimWorkflowFieldBinding::setDescription( const QString& description )
|
||||
{
|
||||
m_description = description;
|
||||
}
|
||||
|
||||
void RimWorkflowFieldBinding::setRequired( bool required )
|
||||
{
|
||||
m_required = required;
|
||||
}
|
||||
|
||||
void RimWorkflowFieldBinding::applySchema( const QJsonObject& fieldSchema )
|
||||
{
|
||||
setFieldName( fieldSchema.value( "name" ).toString() );
|
||||
setDescription( fieldSchema.value( "description" ).toString() );
|
||||
setRequired( fieldSchema.value( "required" ).toBool( false ) );
|
||||
|
||||
if ( auto* vf = valueField() )
|
||||
{
|
||||
vf->uiCapability()->setUiName( m_fieldName() );
|
||||
if ( !m_description().isEmpty() ) vf->uiCapability()->setUiToolTip( m_description() );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "cafPdmField.h"
|
||||
#include "cafPdmObject.h"
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
class RimWorkflowFieldBinding : public caf::PdmObject
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimWorkflowFieldBinding();
|
||||
|
||||
QString fieldName() const;
|
||||
void setFieldName( const QString& name );
|
||||
void setDescription( const QString& description );
|
||||
void setRequired( bool required );
|
||||
|
||||
virtual void applySchema( const QJsonObject& fieldSchema );
|
||||
virtual QString toYamlValue() const = 0;
|
||||
|
||||
virtual caf::PdmFieldHandle* valueField() = 0;
|
||||
|
||||
protected:
|
||||
caf::PdmField<QString> m_fieldName;
|
||||
caf::PdmField<QString> m_description;
|
||||
caf::PdmField<bool> m_required;
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowFilePathBinding.h"
|
||||
|
||||
#include "cafPdmUiFilePathEditor.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimWorkflowFilePathBinding, "WorkflowFilePathBinding" );
|
||||
|
||||
RimWorkflowFilePathBinding::RimWorkflowFilePathBinding()
|
||||
: m_selectDirectory( false )
|
||||
{
|
||||
CAF_PDM_InitFieldNoDefault( &m_value, "Value", "Value" );
|
||||
m_value.uiCapability()->setUiEditorTypeName( caf::PdmUiFilePathEditor::uiEditorTypeName() );
|
||||
}
|
||||
|
||||
void RimWorkflowFilePathBinding::applySchema( const QJsonObject& fieldSchema )
|
||||
{
|
||||
RimWorkflowFieldBinding::applySchema( fieldSchema );
|
||||
|
||||
const QString fmt = fieldSchema.value( "format" ).toString();
|
||||
m_selectDirectory = ( fmt == "directory-path" );
|
||||
|
||||
if ( fieldSchema.contains( "default" ) )
|
||||
{
|
||||
m_value = fieldSchema.value( "default" ).toString();
|
||||
}
|
||||
}
|
||||
|
||||
QString RimWorkflowFilePathBinding::toYamlValue() const
|
||||
{
|
||||
QString p = m_value().path();
|
||||
p.replace( "\\", "\\\\" );
|
||||
p.replace( "\"", "\\\"" );
|
||||
return QString( "\"%1\"" ).arg( p );
|
||||
}
|
||||
|
||||
void RimWorkflowFilePathBinding::defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute )
|
||||
{
|
||||
if ( field == &m_value )
|
||||
{
|
||||
if ( auto* attr = dynamic_cast<caf::PdmUiFilePathEditorAttribute*>( attribute ) )
|
||||
{
|
||||
attr->m_selectDirectory = m_selectDirectory;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowFieldBinding.h"
|
||||
|
||||
#include "cafFilePath.h"
|
||||
|
||||
class RimWorkflowFilePathBinding : public RimWorkflowFieldBinding
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimWorkflowFilePathBinding();
|
||||
|
||||
void applySchema( const QJsonObject& fieldSchema ) override;
|
||||
QString toYamlValue() const override;
|
||||
|
||||
caf::PdmFieldHandle* valueField() override { return &m_value; }
|
||||
|
||||
protected:
|
||||
void defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override;
|
||||
|
||||
private:
|
||||
caf::PdmField<caf::FilePath> m_value;
|
||||
bool m_selectDirectory;
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowFloatBinding.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimWorkflowFloatBinding, "WorkflowFloatBinding" );
|
||||
|
||||
RimWorkflowFloatBinding::RimWorkflowFloatBinding()
|
||||
{
|
||||
CAF_PDM_InitField( &m_value, "Value", 0.0, "Value" );
|
||||
}
|
||||
|
||||
void RimWorkflowFloatBinding::applySchema( const QJsonObject& fieldSchema )
|
||||
{
|
||||
RimWorkflowFieldBinding::applySchema( fieldSchema );
|
||||
if ( fieldSchema.contains( "default" ) )
|
||||
{
|
||||
m_value = fieldSchema.value( "default" ).toDouble();
|
||||
}
|
||||
}
|
||||
|
||||
QString RimWorkflowFloatBinding::toYamlValue() const
|
||||
{
|
||||
return QString::number( m_value(), 'g', 17 );
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowFieldBinding.h"
|
||||
|
||||
class RimWorkflowFloatBinding : public RimWorkflowFieldBinding
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimWorkflowFloatBinding();
|
||||
|
||||
void applySchema( const QJsonObject& fieldSchema ) override;
|
||||
QString toYamlValue() const override;
|
||||
|
||||
caf::PdmFieldHandle* valueField() override { return &m_value; }
|
||||
|
||||
private:
|
||||
caf::PdmField<double> m_value;
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowIntBinding.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimWorkflowIntBinding, "WorkflowIntBinding" );
|
||||
|
||||
RimWorkflowIntBinding::RimWorkflowIntBinding()
|
||||
{
|
||||
CAF_PDM_InitField( &m_value, "Value", 0, "Value" );
|
||||
}
|
||||
|
||||
void RimWorkflowIntBinding::applySchema( const QJsonObject& fieldSchema )
|
||||
{
|
||||
RimWorkflowFieldBinding::applySchema( fieldSchema );
|
||||
if ( fieldSchema.contains( "default" ) )
|
||||
{
|
||||
m_value = fieldSchema.value( "default" ).toInt();
|
||||
}
|
||||
}
|
||||
|
||||
QString RimWorkflowIntBinding::toYamlValue() const
|
||||
{
|
||||
return QString::number( m_value() );
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowFieldBinding.h"
|
||||
|
||||
class RimWorkflowIntBinding : public RimWorkflowFieldBinding
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimWorkflowIntBinding();
|
||||
|
||||
void applySchema( const QJsonObject& fieldSchema ) override;
|
||||
QString toYamlValue() const override;
|
||||
|
||||
caf::PdmFieldHandle* valueField() override { return &m_value; }
|
||||
|
||||
private:
|
||||
caf::PdmField<int> m_value;
|
||||
};
|
||||
@@ -0,0 +1,195 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowJob.h"
|
||||
|
||||
#include "RimWorkflow.h"
|
||||
|
||||
#include "RiaApplication.h"
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include "RiuMainWindow.h"
|
||||
#include "RiuWorkflowJobRunner.h"
|
||||
|
||||
#include "cafCmdFeatureMenuBuilder.h"
|
||||
#include "cafPdmUiGroup.h"
|
||||
#include "cafPdmUiOrdering.h"
|
||||
#include "cafPdmUiTreeOrdering.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QUuid>
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimWorkflowJob, "WorkflowJob" );
|
||||
|
||||
RimWorkflowJob::RimWorkflowJob()
|
||||
{
|
||||
CAF_PDM_InitObject( "Job", ":/Bullet.png" );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_name, "Name", "Name" );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_taskInputs, "TaskInputs", "" );
|
||||
}
|
||||
|
||||
void RimWorkflowJob::setJobName( const QString& name )
|
||||
{
|
||||
m_name = name;
|
||||
setUiName( name );
|
||||
}
|
||||
|
||||
void RimWorkflowJob::initAfterRead()
|
||||
{
|
||||
setUiName( m_name() );
|
||||
}
|
||||
|
||||
void RimWorkflowJob::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
|
||||
{
|
||||
uiOrdering.add( &m_name );
|
||||
|
||||
for ( RimWorkflowTaskInput* task : m_taskInputs.childrenByType() )
|
||||
{
|
||||
if ( !task ) continue;
|
||||
caf::PdmUiGroup* group = uiOrdering.addNewGroup( task->taskName() );
|
||||
for ( RimWorkflowFieldBinding* b : task->items() )
|
||||
{
|
||||
if ( b && b->valueField() ) group->add( b->valueField() );
|
||||
}
|
||||
}
|
||||
uiOrdering.skipRemainingFields( true );
|
||||
}
|
||||
|
||||
void RimWorkflowJob::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName )
|
||||
{
|
||||
uiTreeOrdering.skipRemainingChildren( true );
|
||||
}
|
||||
|
||||
std::vector<RimWorkflowTaskInput*> RimWorkflowJob::taskInputs() const
|
||||
{
|
||||
std::vector<RimWorkflowTaskInput*> result;
|
||||
result.reserve( m_taskInputs.size() );
|
||||
for ( RimWorkflowTaskInput* t : m_taskInputs.childrenByType() )
|
||||
{
|
||||
if ( t ) result.push_back( t );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void RimWorkflowJob::setTaskInputs( std::vector<RimWorkflowTaskInput*> inputs )
|
||||
{
|
||||
m_taskInputs.deleteChildren();
|
||||
for ( RimWorkflowTaskInput* t : inputs )
|
||||
{
|
||||
if ( t ) m_taskInputs.push_back( t );
|
||||
}
|
||||
}
|
||||
|
||||
void RimWorkflowJob::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
|
||||
{
|
||||
if ( changedField == &m_name )
|
||||
{
|
||||
setUiName( m_name() );
|
||||
uiCapability()->updateConnectedEditors();
|
||||
}
|
||||
}
|
||||
|
||||
void RimWorkflowJob::appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const
|
||||
{
|
||||
menuBuilder << "RicRunWorkflowJobFeature";
|
||||
menuBuilder << "RicCancelWorkflowJobFeature";
|
||||
}
|
||||
|
||||
bool RimWorkflowJob::isRunning() const
|
||||
{
|
||||
return m_runner && m_runner->isRunning();
|
||||
}
|
||||
|
||||
void RimWorkflowJob::cancelJob()
|
||||
{
|
||||
if ( m_runner ) m_runner->cancel();
|
||||
}
|
||||
|
||||
QString RimWorkflowJob::writeInputYaml( const QString& path ) const
|
||||
{
|
||||
QString body;
|
||||
for ( RimWorkflowTaskInput* t : m_taskInputs.childrenByType() )
|
||||
{
|
||||
if ( t ) body += t->toTaskYamlBlock();
|
||||
}
|
||||
|
||||
QFile out( path );
|
||||
if ( !out.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) return {};
|
||||
out.write( body.toUtf8() );
|
||||
out.close();
|
||||
return path;
|
||||
}
|
||||
|
||||
void RimWorkflowJob::runJob()
|
||||
{
|
||||
if ( isRunning() )
|
||||
{
|
||||
RiaLogging::warning( "Job is already running." );
|
||||
return;
|
||||
}
|
||||
|
||||
auto* workflow = firstAncestorOrThisOfType<RimWorkflow>();
|
||||
if ( !workflow )
|
||||
{
|
||||
RiaLogging::warning( "Cannot run job: parent workflow not found." );
|
||||
return;
|
||||
}
|
||||
|
||||
const QString dir = workflow->workflowDirectory();
|
||||
if ( dir.isEmpty() ) return;
|
||||
|
||||
auto port = RiaApplication::instance()->activeGrpcPortNumber();
|
||||
if ( !port.has_value() )
|
||||
{
|
||||
RiaLogging::warning( "Cannot run workflow: gRPC server is not active. Enable it in Preferences." );
|
||||
return;
|
||||
}
|
||||
|
||||
QString python = RimWorkflow::findPythonExecutable();
|
||||
if ( python.isEmpty() )
|
||||
{
|
||||
RiaLogging::warning( "Cannot run workflow: no Python interpreter found." );
|
||||
return;
|
||||
}
|
||||
|
||||
QDir tmp( QDir::tempPath() );
|
||||
QString runDir = QString( "resinsight_workflow_%1" ).arg( QUuid::createUuid().toString( QUuid::WithoutBraces ) );
|
||||
if ( !tmp.mkpath( runDir ) )
|
||||
{
|
||||
RiaLogging::warning( "Cannot create temp dir for workflow run." );
|
||||
return;
|
||||
}
|
||||
QString inputPath = tmp.absoluteFilePath( runDir + "/input.yaml" );
|
||||
if ( writeInputYaml( inputPath ).isEmpty() )
|
||||
{
|
||||
RiaLogging::warning( "Failed to write input.yaml" );
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList args{ "-m", "rips.taskmaestro_helper", "run", dir, "--input", inputPath, "--grpc-port", QString::number( port.value() ) };
|
||||
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
const QString label = workflow->uiName() + " / " + m_name();
|
||||
|
||||
m_runner = new RiuWorkflowJobRunner( label, RiuMainWindow::instance() );
|
||||
m_runner->start( python, args, env );
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowTaskInput.h"
|
||||
|
||||
#include "cafPdmChildArrayField.h"
|
||||
#include "cafPdmField.h"
|
||||
#include "cafPdmObject.h"
|
||||
|
||||
#include <QPointer>
|
||||
|
||||
class RiuWorkflowJobRunner;
|
||||
|
||||
class RimWorkflowJob : public caf::PdmObject
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimWorkflowJob();
|
||||
|
||||
void setJobName( const QString& name );
|
||||
|
||||
std::vector<RimWorkflowTaskInput*> taskInputs() const;
|
||||
void setTaskInputs( std::vector<RimWorkflowTaskInput*> inputs );
|
||||
|
||||
QString writeInputYaml( const QString& path ) const;
|
||||
void runJob();
|
||||
void cancelJob();
|
||||
bool isRunning() const;
|
||||
|
||||
protected:
|
||||
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
|
||||
void initAfterRead() override;
|
||||
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
|
||||
void defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName = "" ) override;
|
||||
void appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const override;
|
||||
|
||||
private:
|
||||
caf::PdmField<QString> m_name;
|
||||
caf::PdmChildArrayField<RimWorkflowTaskInput*> m_taskInputs;
|
||||
QPointer<RiuWorkflowJobRunner> m_runner;
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowStringBinding.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimWorkflowStringBinding, "WorkflowStringBinding" );
|
||||
|
||||
RimWorkflowStringBinding::RimWorkflowStringBinding()
|
||||
{
|
||||
CAF_PDM_InitFieldNoDefault( &m_value, "Value", "Value" );
|
||||
}
|
||||
|
||||
void RimWorkflowStringBinding::applySchema( const QJsonObject& fieldSchema )
|
||||
{
|
||||
RimWorkflowFieldBinding::applySchema( fieldSchema );
|
||||
if ( fieldSchema.contains( "default" ) )
|
||||
{
|
||||
m_value = fieldSchema.value( "default" ).toString();
|
||||
}
|
||||
}
|
||||
|
||||
QString RimWorkflowStringBinding::toYamlValue() const
|
||||
{
|
||||
QString v = m_value();
|
||||
v.replace( "\\", "\\\\" );
|
||||
v.replace( "\"", "\\\"" );
|
||||
return QString( "\"%1\"" ).arg( v );
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowFieldBinding.h"
|
||||
|
||||
class RimWorkflowStringBinding : public RimWorkflowFieldBinding
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimWorkflowStringBinding();
|
||||
|
||||
void applySchema( const QJsonObject& fieldSchema ) override;
|
||||
QString toYamlValue() const override;
|
||||
|
||||
caf::PdmFieldHandle* valueField() override { return &m_value; }
|
||||
|
||||
private:
|
||||
caf::PdmField<QString> m_value;
|
||||
};
|
||||
@@ -0,0 +1,119 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowTaskInput.h"
|
||||
|
||||
#include "RimWorkflowBoolBinding.h"
|
||||
#include "RimWorkflowCaseBinding.h"
|
||||
#include "RimWorkflowDateBinding.h"
|
||||
#include "RimWorkflowFilePathBinding.h"
|
||||
#include "RimWorkflowFloatBinding.h"
|
||||
#include "RimWorkflowIntBinding.h"
|
||||
#include "RimWorkflowStringBinding.h"
|
||||
#include "RimWorkflowViewBinding.h"
|
||||
#include "RimWorkflowWellPathBinding.h"
|
||||
|
||||
#include "cafPdmUiOrdering.h"
|
||||
#include "cafPdmUiTreeOrdering.h"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimWorkflowTaskInput, "WorkflowTaskInput" );
|
||||
|
||||
namespace
|
||||
{
|
||||
RimWorkflowFieldBinding* createBinding( const QJsonObject& schema )
|
||||
{
|
||||
const QString resinsightType = schema.value( "resinsight_type" ).toString();
|
||||
if ( resinsightType == "EclipseCase" ) return new RimWorkflowCaseBinding;
|
||||
if ( resinsightType == "WellPath" ) return new RimWorkflowWellPathBinding;
|
||||
if ( resinsightType == "View" ) return new RimWorkflowViewBinding;
|
||||
|
||||
const QString type = schema.value( "type" ).toString( "string" );
|
||||
const QString format = schema.value( "format" ).toString();
|
||||
if ( type == "string" && format == "date" ) return new RimWorkflowDateBinding;
|
||||
if ( type == "string" && ( format == "path" || format == "file-path" || format == "directory-path" ) )
|
||||
return new RimWorkflowFilePathBinding;
|
||||
if ( type == "boolean" ) return new RimWorkflowBoolBinding;
|
||||
if ( type == "integer" ) return new RimWorkflowIntBinding;
|
||||
if ( type == "number" ) return new RimWorkflowFloatBinding;
|
||||
return new RimWorkflowStringBinding;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
RimWorkflowTaskInput::RimWorkflowTaskInput()
|
||||
{
|
||||
CAF_PDM_InitObject( "Task", ":/Bullet.png" );
|
||||
CAF_PDM_InitFieldNoDefault( &m_items, "Bindings", "" );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_taskName, "TaskName", "Task" );
|
||||
m_taskName.uiCapability()->setUiReadOnly( true );
|
||||
}
|
||||
|
||||
QString RimWorkflowTaskInput::taskName() const
|
||||
{
|
||||
return m_taskName();
|
||||
}
|
||||
|
||||
void RimWorkflowTaskInput::setTaskName( const QString& name )
|
||||
{
|
||||
m_taskName = name;
|
||||
setUiName( name );
|
||||
}
|
||||
|
||||
void RimWorkflowTaskInput::buildFromSchema( const QJsonArray& configFields )
|
||||
{
|
||||
deleteAllItems();
|
||||
for ( const QJsonValue& v : configFields )
|
||||
{
|
||||
const QJsonObject schema = v.toObject();
|
||||
auto* binding = createBinding( schema );
|
||||
binding->applySchema( schema );
|
||||
addItem( binding );
|
||||
}
|
||||
}
|
||||
|
||||
void RimWorkflowTaskInput::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
|
||||
{
|
||||
for ( RimWorkflowFieldBinding* b : items() )
|
||||
{
|
||||
if ( b && b->valueField() ) uiOrdering.add( b->valueField() );
|
||||
}
|
||||
uiOrdering.skipRemainingFields( true );
|
||||
}
|
||||
|
||||
void RimWorkflowTaskInput::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName )
|
||||
{
|
||||
uiTreeOrdering.skipRemainingChildren( true );
|
||||
}
|
||||
|
||||
QString RimWorkflowTaskInput::toTaskYamlBlock() const
|
||||
{
|
||||
QString out = m_taskName() + ":\n";
|
||||
if ( count() == 0 )
|
||||
{
|
||||
out += " {}\n";
|
||||
return out;
|
||||
}
|
||||
for ( const RimWorkflowFieldBinding* b : items() )
|
||||
{
|
||||
out += QString( " %1: %2\n" ).arg( b->fieldName(), b->toYamlValue() );
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowFieldBinding.h"
|
||||
|
||||
#include "cafPdmObjectCollection.h"
|
||||
|
||||
class QJsonArray;
|
||||
|
||||
class RimWorkflowTaskInput : public caf::PdmObjectCollection<RimWorkflowFieldBinding>
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimWorkflowTaskInput();
|
||||
|
||||
QString taskName() const;
|
||||
void setTaskName( const QString& name );
|
||||
|
||||
void buildFromSchema( const QJsonArray& configFields );
|
||||
|
||||
QString toTaskYamlBlock() const;
|
||||
|
||||
protected:
|
||||
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
|
||||
void defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName = "" ) override;
|
||||
|
||||
private:
|
||||
caf::PdmField<QString> m_taskName;
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowViewBinding.h"
|
||||
|
||||
#include "RimEclipseCase.h"
|
||||
#include "RimEclipseCaseTools.h"
|
||||
#include "RimEclipseView.h"
|
||||
#include "RimProject.h"
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimWorkflowViewBinding, "WorkflowViewBinding" );
|
||||
|
||||
RimWorkflowViewBinding::RimWorkflowViewBinding()
|
||||
{
|
||||
CAF_PDM_InitFieldNoDefault( &m_view, "View", "View" );
|
||||
}
|
||||
|
||||
QString RimWorkflowViewBinding::toYamlValue() const
|
||||
{
|
||||
if ( m_view() == nullptr ) return "null";
|
||||
return QString( "{__resinsight_ref__: View, view_id: %1}" ).arg( m_view()->id() );
|
||||
}
|
||||
|
||||
QList<caf::PdmOptionItemInfo> RimWorkflowViewBinding::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions )
|
||||
{
|
||||
QList<caf::PdmOptionItemInfo> options;
|
||||
if ( fieldNeedingOptions != &m_view ) return options;
|
||||
|
||||
auto* project = RimProject::current();
|
||||
if ( project == nullptr ) return options;
|
||||
|
||||
for ( RimEclipseCase* eclipseCase : RimEclipseCaseTools::eclipseCases() )
|
||||
{
|
||||
if ( eclipseCase == nullptr ) continue;
|
||||
for ( RimEclipseView* view : eclipseCase->reservoirViews() )
|
||||
{
|
||||
if ( view == nullptr ) continue;
|
||||
QString label = QString( "%1 / %2" ).arg( eclipseCase->caseUserDescription(), view->name() );
|
||||
options.push_back( caf::PdmOptionItemInfo( label, view ) );
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowFieldBinding.h"
|
||||
|
||||
#include "cafPdmPtrField.h"
|
||||
|
||||
class RimEclipseView;
|
||||
|
||||
class RimWorkflowViewBinding : public RimWorkflowFieldBinding
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimWorkflowViewBinding();
|
||||
|
||||
QString toYamlValue() const override;
|
||||
|
||||
caf::PdmFieldHandle* valueField() override { return &m_view; }
|
||||
|
||||
private:
|
||||
QList<caf::PdmOptionItemInfo> calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override;
|
||||
|
||||
caf::PdmPtrField<RimEclipseView*> m_view;
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowWellPathBinding.h"
|
||||
|
||||
#include "RimTools.h"
|
||||
#include "RimWellPath.h"
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimWorkflowWellPathBinding, "WorkflowWellPathBinding" );
|
||||
|
||||
RimWorkflowWellPathBinding::RimWorkflowWellPathBinding()
|
||||
{
|
||||
CAF_PDM_InitFieldNoDefault( &m_wellPath, "WellPath", "Well Path" );
|
||||
}
|
||||
|
||||
QString RimWorkflowWellPathBinding::toYamlValue() const
|
||||
{
|
||||
if ( m_wellPath() == nullptr ) return "null";
|
||||
QString name = m_wellPath()->name();
|
||||
name.replace( "\"", "\\\"" );
|
||||
return QString( "{__resinsight_ref__: WellPath, well_path_name: \"%1\"}" ).arg( name );
|
||||
}
|
||||
|
||||
QList<caf::PdmOptionItemInfo> RimWorkflowWellPathBinding::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions )
|
||||
{
|
||||
QList<caf::PdmOptionItemInfo> options;
|
||||
if ( fieldNeedingOptions == &m_wellPath ) RimTools::wellPathOptionItems( &options );
|
||||
return options;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWorkflowFieldBinding.h"
|
||||
|
||||
#include "cafPdmPtrField.h"
|
||||
|
||||
class RimWellPath;
|
||||
|
||||
class RimWorkflowWellPathBinding : public RimWorkflowFieldBinding
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimWorkflowWellPathBinding();
|
||||
|
||||
QString toYamlValue() const override;
|
||||
|
||||
caf::PdmFieldHandle* valueField() override { return &m_wellPath; }
|
||||
|
||||
private:
|
||||
QList<caf::PdmOptionItemInfo> calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override;
|
||||
|
||||
caf::PdmPtrField<RimWellPath*> m_wellPath;
|
||||
};
|
||||
@@ -1,3 +1,124 @@
|
||||
set(SOURCE_GROUP_HEADER_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuCadNavigation.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuCursors.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuDragDrop.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuFemResultTextBuilder.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuGeoQuestNavigation.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuInterfaceToViewWindow.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuPlotCurveSymbol.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuPlotCurveSymbolImageCreator.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuQwtSymbol.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuPlotCurve.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotCurve.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotCurveDefines.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuPlotItem.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotItem.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuPlotMainWindow.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuMainWindow.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuMainWindowBase.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuMdiArea.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuMdiSubWindow.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuProcessMonitor.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuProjectPropertyView.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuPropertyViewTabWidget.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuPropertyViewWizard.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuPvtPlotPanel.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuPvtPlotUpdater.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuPvtPlotWidget.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuPlotUpdater.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuQwtLinearScaleEngine.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuQwtScalePicker.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuQwtCurvePointTracker.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotWheelZoomer.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotZoomer.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuWidgetDragger.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuRecentFileActionProvider.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuRelativePermeabilityPlotPanel.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuRelativePermeabilityPlotUpdater.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuResultInfoPanel.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuResultQwtPlot.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuResultTextBuilder.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuRmsNavigation.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuSelectionChangedHandler.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/Riu3dSelectionManager.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuSimpleHistogramWidget.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuDockedQwtPlot.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuGridCrossQwtPlot.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuSummaryQwtPlot.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuSummaryPlot.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuTextDialog.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuTimeStepChangedHandler.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuTofAccumulatedPhaseFractionsPlot.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuToolTipMenu.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuTreeViewEventFilter.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuViewer.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuViewerToViewInterface.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuViewerCommands.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuCellAndNncPickEventHandler.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuPickItemInfo.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuWellLogPlot.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuWellLogTrack.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuMultiPlotPage.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuMultiPlotBook.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuSummaryMultiPlotPage.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuSummaryMultiPlotBook.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuPlotWidget.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuPlotAxis.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotWidget.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotLegend.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuPlotAnnotationTool.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuGeoMechXfTensorResultAccessor.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuFemTimeHistoryResultAccessor.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuEditPerforationCollectionWidget.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuAdvancedSnapshotExportWidget.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuWellAllocationPlot.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuFlowCharacteristicsPlot.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuNightchartsWidget.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuMessagePanel.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuMessageDialog.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuWorkflowJobRunner.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuPlotObjectPicker.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuContextMenuLauncher.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuSummaryCurveDefinitionKeywords.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuSummaryVectorSelectionUi.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuSummaryVectorSelectionDialog.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuSummaryVectorSelectionWidgetCreator.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuSummaryQuantityNameInfoProvider.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuExpressionContextMenuManager.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuCalculationsContextMenuManager.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuGridStatisticsHistogramWidget.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuMohrsCirclePlot.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuPlotMainWindowTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/Riu3DMainWindowTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuDockWidgetTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuQwtCurveSelectorFilter.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotItemGroup.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuDepthQwtPlot.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuWellPathComponentPlotItem.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuMeasurementViewEventFilter.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuDraggableOverlayFrame.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuMdiMaximizeWindowGuard.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuMainWindowTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuComparisonViewMover.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuAbstractOverlayContentFrame.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuQwtLegendOverlayContentFrame.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuAbstractLegendFrame.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuSeismicHistogramPanel.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuCategoryLegendFrame.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuScalarMapperLegendFrame.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuFileDialogTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuGuiTheme.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuQssSyntaxHighlighter.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuQwtDateScaleWrapper.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuMatrixPlotWidget.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuMenuBarBuildTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotRectAnnotation.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotZoomerMultiAxes.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuCellSelectionTool.h
|
||||
)
|
||||
|
||||
set(SOURCE_GROUP_SOURCE_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuCadNavigation.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuCursors.cpp
|
||||
@@ -72,6 +193,7 @@ set(SOURCE_GROUP_SOURCE_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuNightchartsWidget.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuMessagePanel.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuMessageDialog.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuWorkflowJobRunner.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuPlotObjectPicker.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuContextMenuLauncher.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiuSummaryVectorSelectionUi.cpp
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RiuWorkflowJobRunner.h"
|
||||
|
||||
RiuWorkflowJobRunner::RiuWorkflowJobRunner( const QString& label, QObject* parent )
|
||||
: QObject( parent )
|
||||
, m_label( label )
|
||||
{
|
||||
connect( &m_process, &QProcess::readyReadStandardOutput, this, &RiuWorkflowJobRunner::onReadyReadStdout );
|
||||
connect( &m_process, &QProcess::readyReadStandardError, this, &RiuWorkflowJobRunner::onReadyReadStderr );
|
||||
connect( &m_process, QOverload<int, QProcess::ExitStatus>::of( &QProcess::finished ), this, &RiuWorkflowJobRunner::onProcessFinished );
|
||||
}
|
||||
|
||||
void RiuWorkflowJobRunner::start( const QString& program, const QStringList& arguments, const QProcessEnvironment& env )
|
||||
{
|
||||
RiaLogging::info( QString( "Running %1" ).arg( m_label ).toStdString() );
|
||||
RiaLogging::info( QString( "[%1] $ %2 %3" ).arg( m_label, program, arguments.join( ' ' ) ).toStdString() );
|
||||
m_process.setProcessEnvironment( env );
|
||||
m_process.start( program, arguments );
|
||||
}
|
||||
|
||||
void RiuWorkflowJobRunner::cancel()
|
||||
{
|
||||
if ( m_process.state() == QProcess::NotRunning ) return;
|
||||
m_process.terminate();
|
||||
if ( !m_process.waitForFinished( 2000 ) ) m_process.kill();
|
||||
}
|
||||
|
||||
bool RiuWorkflowJobRunner::isRunning() const
|
||||
{
|
||||
return m_process.state() != QProcess::NotRunning;
|
||||
}
|
||||
|
||||
void RiuWorkflowJobRunner::onReadyReadStdout()
|
||||
{
|
||||
m_stdoutBuf.append( m_process.readAllStandardOutput() );
|
||||
drainLines( m_stdoutBuf, RILogLevel::RI_LL_INFO );
|
||||
}
|
||||
|
||||
void RiuWorkflowJobRunner::onReadyReadStderr()
|
||||
{
|
||||
m_stderrBuf.append( m_process.readAllStandardError() );
|
||||
drainLines( m_stderrBuf, RILogLevel::RI_LL_WARNING );
|
||||
}
|
||||
|
||||
void RiuWorkflowJobRunner::onProcessFinished( int exitCode, QProcess::ExitStatus status )
|
||||
{
|
||||
m_stdoutBuf.append( m_process.readAllStandardOutput() );
|
||||
m_stderrBuf.append( m_process.readAllStandardError() );
|
||||
|
||||
if ( !m_stdoutBuf.endsWith( '\n' ) ) m_stdoutBuf.append( '\n' );
|
||||
if ( !m_stderrBuf.endsWith( '\n' ) ) m_stderrBuf.append( '\n' );
|
||||
drainLines( m_stdoutBuf, RILogLevel::RI_LL_INFO );
|
||||
drainLines( m_stderrBuf, RILogLevel::RI_LL_WARNING );
|
||||
|
||||
if ( status == QProcess::NormalExit && exitCode == 0 )
|
||||
{
|
||||
RiaLogging::info( QString( "Finished %1 (exit 0)" ).arg( m_label ).toStdString() );
|
||||
}
|
||||
else if ( status == QProcess::NormalExit )
|
||||
{
|
||||
RiaLogging::error( QString( "Finished %1 (exit %2)" ).arg( m_label ).arg( exitCode ).toStdString() );
|
||||
}
|
||||
else
|
||||
{
|
||||
RiaLogging::error( QString( "%1 crashed" ).arg( m_label ).toStdString() );
|
||||
}
|
||||
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void RiuWorkflowJobRunner::drainLines( QByteArray& buffer, RILogLevel level )
|
||||
{
|
||||
int nl;
|
||||
while ( ( nl = buffer.indexOf( '\n' ) ) >= 0 )
|
||||
{
|
||||
QString line = QString::fromUtf8( buffer.left( nl ) );
|
||||
buffer.remove( 0, nl + 1 );
|
||||
if ( line.endsWith( '\r' ) ) line.chop( 1 );
|
||||
if ( line.isEmpty() ) continue;
|
||||
|
||||
const std::string msg = QString( "[%1] %2" ).arg( m_label, line ).toStdString();
|
||||
if ( level == RILogLevel::RI_LL_WARNING )
|
||||
RiaLogging::warning( msg );
|
||||
else
|
||||
RiaLogging::info( msg );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RiaLogging.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QObject>
|
||||
#include <QProcess>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
class RiuWorkflowJobRunner : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
RiuWorkflowJobRunner( const QString& label, QObject* parent = nullptr );
|
||||
|
||||
void start( const QString& program, const QStringList& arguments, const QProcessEnvironment& env );
|
||||
void cancel();
|
||||
bool isRunning() const;
|
||||
|
||||
private slots:
|
||||
void onReadyReadStdout();
|
||||
void onReadyReadStderr();
|
||||
void onProcessFinished( int exitCode, QProcess::ExitStatus status );
|
||||
|
||||
private:
|
||||
void drainLines( QByteArray& buffer, RILogLevel level );
|
||||
|
||||
QString m_label;
|
||||
QProcess m_process;
|
||||
QByteArray m_stdoutBuf;
|
||||
QByteArray m_stderrBuf;
|
||||
};
|
||||
Reference in new issue
Block a user