Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions MORYX-Framework.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
<Project Path="src/Moryx.ControlSystem.ProcessEngine.Web/Moryx.ControlSystem.ProcessEngine.Web.csproj" />
<Project Path="src/Moryx.ControlSystem.ProcessEngine/Moryx.ControlSystem.ProcessEngine.csproj" />
<Project Path="src/Moryx.ControlSystem.Processes.Endpoints/Moryx.ControlSystem.Processes.Endpoints.csproj" />
<Project Path="src/Moryx.ControlSystem.SetupProvider/Moryx.ControlSystem.SetupProvider.csproj" />
<Project Path="src/Moryx.ControlSystem.Simulator/Moryx.ControlSystem.Simulator.csproj" />
</Folder>
<Folder Name="/Drivers/">
Expand Down Expand Up @@ -121,7 +120,6 @@
<Project Path="src/Tests/Moryx.AbstractionLayer.Tests/Moryx.AbstractionLayer.Tests.csproj" />
<Project Path="src/Tests/Moryx.Container.Tests/Moryx.Container.Tests.csproj" />
<Project Path="src/Tests/Moryx.ControlSystem.ProcessEngine.Tests/Moryx.ControlSystem.ProcessEngine.Tests.csproj" />
<Project Path="src/Tests/Moryx.ControlSystem.SetupProvider.Tests/Moryx.ControlSystem.SetupProvider.Tests.csproj" />
<Project Path="src/Tests/Moryx.ControlSystem.Tests/Moryx.ControlSystem.Tests.csproj" />
<Project Path="src/Tests/Moryx.Drivers.Mqtt.Tests/Moryx.Drivers.Mqtt.Tests.csproj" />
<Project Path="src/Tests/Moryx.Drivers.OpcUa.Tests/Moryx.Drivers.OpcUa.Tests.csproj" />
Expand Down Expand Up @@ -181,8 +179,4 @@
<Project Path="src/Moryx.Notifications/Moryx.Notifications.csproj" />
<Project Path="src/Moryx/Moryx.csproj" />
<Project Path="src/StartProject.Asp/StartProject.Asp.csproj" />
<Properties Name="ExtensibilityGlobals" Scope="PostLoad">
<Property Name="RESX_ShowErrorsInErrorList" Value="True" />
<Property Name="RESX_TaskErrorCategory" Value="Message" />
</Properties>
</Solution>
28 changes: 27 additions & 1 deletion docs/migrations/v8_to_v10.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ Several interfaces have been removed to streamline the codebase and reduce compl
- `IProductionRecipe`: Replaced with class `ProductionRecipe`
- `ISetupRecipe`: Replaced with class `SetupRecipe`
- `IState`: Replace with base-class `StateBase`
-

The following interfaces are still existent for api extensions but the base class is used in whole code base:

- `IActivity`: Replaced with class `Activity`
Expand Down Expand Up @@ -421,6 +421,32 @@ The API of `IResourceInitializer` was adjusted
- Removed API from IJobManagement: `JobEvaluation Evaluate(IProductRecipe recipe, int amount, IResourceManagement resourceManagement)`
- Added `IAsyncEnumerable<IProcessChunk> LoadArchivedProcessesAsync(ProcessRequestFilter filterType, DateTime start, DateTime end, long[] jobIds)` to `IProcessControl`

### Integrated SetupProvider

The module SetupProvider was integrated into the ProcessEngine for many reasons:

- The SetupProvider was a optional dependency of the ProcessEngine but it cannot be meaningfully deployed without the ProcessEngine, the separation is a leaky abstraction. \
Merging removes indirection (extra DI registrations, configuration, assembly loading, error handling paths) and simplifies the overall model.
- High coupling with low autonomy is a signal that the components belong together
- Fewer NuGet references, less transitive dependency bloat, fewer binding redirects
- Preconditions that the *SetupProvider should be present* for all features of the ProcessEngine can be enforced as internal invariants rather than scattered runtime checks.

**Upgrade hint:** Move SetupTriggers config-section from the `Moryx.ControlSystem.SetupProvider.ModuleConfig.json` to `Moryx.ControlSystem.ProcessEngine.ModuleConfig.json`

````json
{
"SetupTriggers": [
...
],
...
}

````

## Modules-SetupProvider

The SetupProvider was integrated into the ProcessEngine module.

## Modules-Media

- Facade Renaming:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
// Licensed under the Apache License, Version 2.0

using Moryx.AbstractionLayer.Recipes;
using Moryx.ControlSystem.ProcessEngine.Setups;
using Moryx.ControlSystem.Recipes;
using Moryx.ControlSystem.Setups;
using Moryx.Runtime.Modules;

namespace Moryx.ControlSystem.SetupProvider
namespace Moryx.ControlSystem.ProcessEngine
{
internal class SetupProviderFacade : ISetupProvider, IFacadeControl
{
Expand All @@ -28,23 +29,16 @@ public void Deactivate()
{
}

public string Name => "SetupProvider";
public string Name => SetupManager.Name;

public Task<IRecipe> LoadRecipeAsync(long id, CancellationToken cancellationToken = default)
{
ValidateHealthState();

// TODO: Setup restore --> SetupRecipes are not saved
return Task.FromResult<IRecipe>(new SetupRecipe
{
Id = id,
Origin = this
});
return SetupManager.LoadRecipeAsync(id, cancellationToken);
}

public SetupRecipe RequiredSetup(SetupExecution execution, ProductionRecipe recipe, ISetupTarget targetSystem)
{

ValidateHealthState();

var setup = SetupManager.RequiredSetup(execution, recipe, targetSystem);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.Extensions.Logging;
using Moryx.Container;
using Moryx.ControlSystem.Jobs;
using Moryx.ControlSystem.Setups;
using Moryx.Logging;
using Moryx.Tools;

Expand Down Expand Up @@ -70,15 +71,10 @@ private void AwaitCleanJobList()
Thread.Sleep(1);

if (_jobs.Any(j => !j.IsStable))
Logger.LogWarning("Reached shutdown-timout of the {name} after {seconds}s. Unfinished jobs: {jobs}",
Logger.LogWarning("Reached shutdown-timeout of the {name} after {seconds}s. Unfinished jobs: {jobs}",
nameof(JobList), Config.JobListStopTimeout, string.Join(", ", _jobs.Select(j => j.Id)));
}

/// <inheritdoc cref="IJobDataList"/>
public void Dispose()
{
}

#endregion

/// <inheritdoc cref="IJobDataList"/>
Expand Down Expand Up @@ -306,13 +302,13 @@ private IReadOnlyList<IJobData> ExecuteAdd(LinkedList<IJobData> jobDatas, JobPos
// TODO: This solution only fixes a special case and needs to be replaced with proper job positioning
if (referenceNode.Value.Recipe.Id != jobDatas.First().Recipe.Id
&& referenceNode.Previous?.Value is ISetupJobData setup
&& setup.Recipe.Execution == Setups.SetupExecution.BeforeProduction
&& setup.Recipe.Execution == SetupExecution.BeforeProduction
&& setup.Recipe.TargetRecipe.Id == referenceNode.Value.Recipe.Id)
referenceNode = referenceNode.Previous;

if (referenceNode.Value.Recipe.Id != jobDatas.First().Recipe.Id
&& referenceNode.Previous?.Value is ISetupJobData cleanup
&& cleanup.Recipe.Execution == Setups.SetupExecution.AfterProduction
&& cleanup.Recipe.Execution == SetupExecution.AfterProduction
&& cleanup.Recipe.TargetRecipe.Id == jobDatas.Last().Recipe.Id)
referenceNode = referenceNode.Previous;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Moryx.ControlSystem.ProcessEngine.Jobs.Setup;
using Moryx.ControlSystem.ProcessEngine.Model;
using Moryx.ControlSystem.ProcessEngine.Processes;
using Moryx.ControlSystem.ProcessEngine.Setups;
using Moryx.ControlSystem.Setups;
using ProcessContext = Moryx.ControlSystem.ProcessEngine.Model.ProcessContext;
using Microsoft.Extensions.Logging;
Expand Down Expand Up @@ -40,7 +41,7 @@ internal class JobStorage : IJobStorage, IJobHistory, ILoggingComponent
/// <summary>
/// Setup provider
/// </summary>
public ISetupProvider SetupProvider { get; set; }
public ISetupManager SetupManager { get; set; }

/// <summary>
/// Recipe provider for temporary clean-up
Expand All @@ -50,8 +51,7 @@ internal class JobStorage : IJobStorage, IJobHistory, ILoggingComponent
/// <summary>
/// All recipe providers
/// </summary>
public IEnumerable<IRecipeProvider> RecipeProviders => SetupProvider == null
? new IRecipeProvider[] { ProductManagement, CleanupProvider } : [ProductManagement, CleanupProvider, SetupProvider];
public IEnumerable<IRecipeProvider> RecipeProviders => [ProductManagement, CleanupProvider, SetupManager];

/// <summary>
/// Unit of work factory to open a database context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Moryx.Container;
using Moryx.ControlSystem.Cells;
using Moryx.ControlSystem.Jobs;
using Moryx.ControlSystem.ProcessEngine.Setups;
using Moryx.ControlSystem.Recipes;
using Moryx.ControlSystem.Setups;
using Moryx.Logging;
Expand All @@ -30,10 +31,9 @@ internal class SetupJobHandler : ILoggingComponent, ISetupJobHandler
public IJobDataList JobList { get; set; }

/// <summary>
/// Castle factory to create <see cref="ISetupTrigger"/> instances from
/// their <see cref="SetupTriggerConfig"/>
/// Component that can determine setup requirement and create setup recipes
/// </summary>
public ISetupProvider SetupProvider { get; set; }
public ISetupManager SetupManager { get; set; }

/// <summary>
/// Temporary recipe provider
Expand Down Expand Up @@ -92,7 +92,7 @@ private LinkedListNode<IJobData> HandleCurrentJob(LinkedList<IJobData> newJobs,
SetupRecipe prepareRecipe;
try
{
prepareRecipe = SetupProvider?.RequiredSetup(SetupExecution.BeforeProduction, productionJob.Recipe, new CurrentResourceTarget(ResourceManagement));
prepareRecipe = SetupManager.RequiredSetup(SetupExecution.BeforeProduction, productionJob.Recipe, new CurrentResourceTarget(ResourceManagement));
}
catch (Exception setupProviderException)
{
Expand All @@ -112,7 +112,7 @@ private LinkedListNode<IJobData> HandleCurrentJob(LinkedList<IJobData> newJobs,
try
{
// Clean-up is evaluated just in time, so we only create a temporary recipe
cleanupRecipe = SetupProvider?.RequiredSetup(SetupExecution.AfterProduction, productionJob.Recipe, new TemporaryCleanupTarget()) ??
cleanupRecipe = SetupManager.RequiredSetup(SetupExecution.AfterProduction, productionJob.Recipe, new TemporaryCleanupTarget()) ??
RecipeProvider.CreateTemporary(productionJob.Recipe);
}
catch (Exception e)
Expand Down Expand Up @@ -204,7 +204,7 @@ private bool LastOfRecipe(LinkedList<IJobData> newJobs, IJobData currentNode)
private void LogAbortedJobs(Exception e, List<long> abortedJobIds, SetupExecution executionType)
{
Logger.LogError(e, "{provider} threw an exception when creating required setup {classification} for " +
"job(s) {jobs}. Interrupting job(s)...", SetupProvider.GetType().Name, executionType, string.Join(", ", abortedJobIds));
"job(s) {jobs}. Interrupting job(s)...", SetupManager.GetType().Name, executionType, string.Join(", ", abortedJobIds));
}

/// <summary>
Expand Down Expand Up @@ -282,7 +282,7 @@ private void CheckRecipeUpdate(IJobData jobData)

// Fetch current recipe and check if setup is complete
var currentRecipe = setupJob.Recipe;
var retryRecipe = SetupProvider?.RequiredSetup(currentRecipe.Execution, (ProductionRecipe)currentRecipe.TargetRecipe, new CurrentResourceTarget(ResourceManagement));
var retryRecipe = SetupManager.RequiredSetup(currentRecipe.Execution, (ProductionRecipe)currentRecipe.TargetRecipe, new CurrentResourceTarget(ResourceManagement));
setupJob.UpdateSetup(retryRecipe);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Moryx.ControlSystem.ProcessEngine.Jobs;
using Moryx.ControlSystem.ProcessEngine.Jobs.Setup;
using Moryx.ControlSystem.ProcessEngine.Processes;
using Moryx.ControlSystem.ProcessEngine.Setups;
using Moryx.Logging;

namespace Moryx.ControlSystem.ProcessEngine
Expand Down Expand Up @@ -37,19 +38,23 @@ internal class ComponentOrchestration

public IActivityPoolListener[] PoolListeners { get; set; }

public ISetupJobHandler SetupManager { get; set; }
public ISetupJobHandler SetupJobHandler { get; set; }

public ISetupManager SetupManager { get; set; }

public IModuleLogger Logger { get; set; }

#endregion

public void Start()
{
SetupManager.Start();

JobStorage.Start();

JobScheduler.Initialize(Config.JobSchedulerConfig);

SetupManager.Start();
SetupJobHandler.Start();

ProcessStorage.Start();

Expand Down Expand Up @@ -85,7 +90,7 @@ public void Stop()
Logger.LogError(e, "Shutting down process engine caused an exception in {name}", JobScheduler.GetType().Name);
}

SetupManager.Stop();
SetupJobHandler.Stop();
JobDispatcher.Stop();

JobManager.Stop();
Expand All @@ -100,6 +105,8 @@ public void Stop()
JobList.Stop();

JobStorage.Stop();

SetupManager.Stop();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Moryx.ControlSystem.Cells;
using Moryx.ControlSystem.Jobs;
using Moryx.ControlSystem.ProcessEngine.Jobs;
using Moryx.ControlSystem.Setups;
using Moryx.Notifications;
using Moryx.Runtime.Configuration;
using Moryx.Serialization;
Expand Down Expand Up @@ -117,6 +118,12 @@ public ModuleConfig()
[DataMember, DefaultValue(DefaultSetupJobRetryLimit)]
[Description("Limit of retries for a failed setup job. Smaller than 0 for infinite.")]
public int SetupJobRetryLimit { get; set; }

/// <summary>
/// All configured setup triggers for this application
/// </summary>
[DataMember, PluginConfigs(typeof(ISetupTrigger))]
public List<SetupTriggerConfig> SetupTriggers { get; set; }
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,15 @@ namespace Moryx.ControlSystem.ProcessEngine
"Job execution is influenced by a jobs constraint and evaluated by dedicated components of the DefaultJobScheduler. " +
"Jobs may include but are not limited to production, maintenance and resource configuration.")]
public class ModuleController : ServerModuleBase<ModuleConfig>,
IFacadeContainer<IJobManagement>, IFacadeContainer<INotificationSource>, IFacadeContainer<IProcessControl>
IFacadeContainer<IJobManagement>,
IFacadeContainer<INotificationSource>,
IFacadeContainer<IProcessControl>,
IFacadeContainer<ISetupProvider>
{
/// <summary>
/// The module's name.
/// </summary>
public const string ModuleName = "ProcessEngine";
private const string ModuleName = "ProcessEngine";

/// <inheritdoc />
public override string Name => ModuleName;
Expand All @@ -55,13 +58,7 @@ public ModuleController(IModuleContainerFactory containerFactory, IConfigManager
/// </summary>
[RequiredModuleApi(IsStartDependency = true, IsOptional = false)]
public IResourceManagement ResourceManagement { get; set; }

/// <summary>
/// Found recipe providers used to restore recipes links of jobs and processes
/// </summary>
[RequiredModuleApi(IsStartDependency = true, IsOptional = true)]
public ISetupProvider SetupProvider { get; set; }


/// <summary>
/// Product management to load and save articles instances and products
/// </summary>
Expand All @@ -85,9 +82,9 @@ protected override Task OnInitializeAsync(CancellationToken cancellationToken)
// Register all imported components
Container.SetInstance(ResourceManagement)
.SetInstance(ProductManagement);
// Register optional setup dependency
if (SetupProvider != null)
Container.SetInstance(SetupProvider);

// Register plugins for the setup management
Container.LoadComponents<ISetupTrigger>();

// Register process plugins
Container.LoadComponents<ICellSelector>();
Expand All @@ -105,6 +102,7 @@ protected override Task OnInitializeAsync(CancellationToken cancellationToken)
protected override Task OnStartAsync(CancellationToken cancellationToken)
{
// Activate facade
ActivateFacade(_setupProviderFacade);
ActivateFacade(_jobManagementFacade);
ActivateFacade(_notificationSourceFacade);
ActivateFacade(_processControlFacade);
Expand All @@ -127,6 +125,7 @@ protected override Task OnStopAsync(CancellationToken cancellationToken)
DeactivateFacade(_jobManagementFacade);
DeactivateFacade(_notificationSourceFacade);
DeactivateFacade(_processControlFacade);
DeactivateFacade(_setupProviderFacade);

return Task.CompletedTask;
}
Expand All @@ -145,6 +144,10 @@ protected override Task OnStopAsync(CancellationToken cancellationToken)

IProcessControl IFacadeContainer<IProcessControl>.Facade => _processControlFacade;

private readonly SetupProviderFacade _setupProviderFacade = new();

ISetupProvider IFacadeContainer<ISetupProvider>.Facade => _setupProviderFacade;

#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
using Moryx.ControlSystem.Setups;
using Moryx.Modules;

namespace Moryx.ControlSystem.SetupProvider
namespace Moryx.ControlSystem.ProcessEngine.Setups
{
internal interface ISetupManager : IPlugin
/// <summary>
/// Component that can determine setup requirement and create setup recipes
/// </summary>
internal interface ISetupManager : IPlugin, IRecipeProvider
{
SetupRecipe RequiredSetup(SetupExecution execution, ProductionRecipe recipe, ISetupTarget targetSystem);
}
Expand Down
Loading
Loading