Skip to content

Commit f19fb68

Browse files
committed
Merged SetupProvider into ProcessEngine
1 parent 04e0546 commit f19fb68

30 files changed

Lines changed: 133 additions & 367 deletions

MORYX-Framework.slnx

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
<Project Path="src/Moryx.ControlSystem.ProcessEngine.Web/Moryx.ControlSystem.ProcessEngine.Web.csproj" />
1414
<Project Path="src/Moryx.ControlSystem.ProcessEngine/Moryx.ControlSystem.ProcessEngine.csproj" />
1515
<Project Path="src/Moryx.ControlSystem.Processes.Endpoints/Moryx.ControlSystem.Processes.Endpoints.csproj" />
16-
<Project Path="src/Moryx.ControlSystem.SetupProvider/Moryx.ControlSystem.SetupProvider.csproj" />
1716
<Project Path="src/Moryx.ControlSystem.Simulator/Moryx.ControlSystem.Simulator.csproj" />
1817
</Folder>
1918
<Folder Name="/Drivers/">
@@ -121,7 +120,6 @@
121120
<Project Path="src/Tests/Moryx.AbstractionLayer.Tests/Moryx.AbstractionLayer.Tests.csproj" />
122121
<Project Path="src/Tests/Moryx.Container.Tests/Moryx.Container.Tests.csproj" />
123122
<Project Path="src/Tests/Moryx.ControlSystem.ProcessEngine.Tests/Moryx.ControlSystem.ProcessEngine.Tests.csproj" />
124-
<Project Path="src/Tests/Moryx.ControlSystem.SetupProvider.Tests/Moryx.ControlSystem.SetupProvider.Tests.csproj" />
125123
<Project Path="src/Tests/Moryx.ControlSystem.Tests/Moryx.ControlSystem.Tests.csproj" />
126124
<Project Path="src/Tests/Moryx.Drivers.Mqtt.Tests/Moryx.Drivers.Mqtt.Tests.csproj" />
127125
<Project Path="src/Tests/Moryx.Drivers.OpcUa.Tests/Moryx.Drivers.OpcUa.Tests.csproj" />
@@ -181,8 +179,4 @@
181179
<Project Path="src/Moryx.Notifications/Moryx.Notifications.csproj" />
182180
<Project Path="src/Moryx/Moryx.csproj" />
183181
<Project Path="src/StartProject.Asp/StartProject.Asp.csproj" />
184-
<Properties Name="ExtensibilityGlobals" Scope="PostLoad">
185-
<Property Name="RESX_ShowErrorsInErrorList" Value="True" />
186-
<Property Name="RESX_TaskErrorCategory" Value="Message" />
187-
</Properties>
188182
</Solution>

docs/migrations/v8_to_v10.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ Several interfaces have been removed to streamline the codebase and reduce compl
320320
- `IProductionRecipe`: Replaced with class `ProductionRecipe`
321321
- `ISetupRecipe`: Replaced with class `SetupRecipe`
322322
- `IState`: Replace with base-class `StateBase`
323-
-
323+
324324
The following interfaces are still existent for api extensions but the base class is used in whole code base:
325325

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

424+
### Integrated SetupProvider
425+
426+
The module SetupProvider was integrated into the ProcessEngine for many reasons:
427+
428+
- The SetupProvider was a optional dependency of the ProcessEngine but it cannot be meaningfully deployed without the ProcessEngine, the separation is a leaky abstraction. \
429+
Merging removes indirection (extra DI registrations, configuration, assembly loading, error handling paths) and simplifies the overall model.
430+
- High coupling with low autonomy is a signal that the components belong together
431+
- Fewer NuGet references, less transitive dependency bloat, fewer binding redirects
432+
- Preconditions that the *SetupProvider should be present* for all features of the ProcessEngine can be enforced as internal invariants rather than scattered runtime checks.
433+
434+
**Upgrade hint:** Move SetupTriggers config-section from the `Moryx.ControlSystem.SetupProvider.ModuleConfig.json` to `Moryx.ControlSystem.ProcessEngine.ModuleConfig.json`
435+
436+
````json
437+
{
438+
"SetupTriggers": [
439+
...
440+
],
441+
...
442+
}
443+
444+
````
445+
446+
## Modules-SetupProvider
447+
448+
The SetupProvider was integrated into the ProcessEngine module.
449+
424450
## Modules-Media
425451

426452
- Facade Renaming:

src/Moryx.ControlSystem.SetupProvider/Facades/SetupProviderFacade.cs renamed to src/Moryx.ControlSystem.ProcessEngine/Facades/SetupProviderFacade.cs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
// Licensed under the Apache License, Version 2.0
33

44
using Moryx.AbstractionLayer.Recipes;
5+
using Moryx.ControlSystem.ProcessEngine.Setups;
56
using Moryx.ControlSystem.Recipes;
67
using Moryx.ControlSystem.Setups;
78
using Moryx.Runtime.Modules;
89

9-
namespace Moryx.ControlSystem.SetupProvider
10+
namespace Moryx.ControlSystem.ProcessEngine
1011
{
1112
internal class SetupProviderFacade : ISetupProvider, IFacadeControl
1213
{
@@ -28,23 +29,16 @@ public void Deactivate()
2829
{
2930
}
3031

31-
public string Name => "SetupProvider";
32+
public string Name => SetupManager.Name;
3233

3334
public Task<IRecipe> LoadRecipeAsync(long id, CancellationToken cancellationToken = default)
3435
{
3536
ValidateHealthState();
36-
37-
// TODO: Setup restore --> SetupRecipes are not saved
38-
return Task.FromResult<IRecipe>(new SetupRecipe
39-
{
40-
Id = id,
41-
Origin = this
42-
});
37+
return SetupManager.LoadRecipeAsync(id, cancellationToken);
4338
}
4439

4540
public SetupRecipe RequiredSetup(SetupExecution execution, ProductionRecipe recipe, ISetupTarget targetSystem)
4641
{
47-
4842
ValidateHealthState();
4943

5044
var setup = SetupManager.RequiredSetup(execution, recipe, targetSystem);

src/Moryx.ControlSystem.ProcessEngine/Jobs/Implementation/JobList.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Microsoft.Extensions.Logging;
77
using Moryx.Container;
88
using Moryx.ControlSystem.Jobs;
9+
using Moryx.ControlSystem.Setups;
910
using Moryx.Logging;
1011
using Moryx.Tools;
1112

@@ -70,15 +71,10 @@ private void AwaitCleanJobList()
7071
Thread.Sleep(1);
7172

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

77-
/// <inheritdoc cref="IJobDataList"/>
78-
public void Dispose()
79-
{
80-
}
81-
8278
#endregion
8379

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

313309
if (referenceNode.Value.Recipe.Id != jobDatas.First().Recipe.Id
314310
&& referenceNode.Previous?.Value is ISetupJobData cleanup
315-
&& cleanup.Recipe.Execution == Setups.SetupExecution.AfterProduction
311+
&& cleanup.Recipe.Execution == SetupExecution.AfterProduction
316312
&& cleanup.Recipe.TargetRecipe.Id == jobDatas.Last().Recipe.Id)
317313
referenceNode = referenceNode.Previous;
318314

src/Moryx.ControlSystem.ProcessEngine/Jobs/Implementation/JobStorage.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using Moryx.ControlSystem.ProcessEngine.Jobs.Setup;
1111
using Moryx.ControlSystem.ProcessEngine.Model;
1212
using Moryx.ControlSystem.ProcessEngine.Processes;
13+
using Moryx.ControlSystem.ProcessEngine.Setups;
1314
using Moryx.ControlSystem.Setups;
1415
using ProcessContext = Moryx.ControlSystem.ProcessEngine.Model.ProcessContext;
1516
using Microsoft.Extensions.Logging;
@@ -40,7 +41,7 @@ internal class JobStorage : IJobStorage, IJobHistory, ILoggingComponent
4041
/// <summary>
4142
/// Setup provider
4243
/// </summary>
43-
public ISetupProvider SetupProvider { get; set; }
44+
public ISetupManager SetupManager { get; set; }
4445

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

5656
/// <summary>
5757
/// Unit of work factory to open a database context

src/Moryx.ControlSystem.ProcessEngine/Jobs/Setup/SetupJobHandler.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using Moryx.Container;
99
using Moryx.ControlSystem.Cells;
1010
using Moryx.ControlSystem.Jobs;
11+
using Moryx.ControlSystem.ProcessEngine.Setups;
1112
using Moryx.ControlSystem.Recipes;
1213
using Moryx.ControlSystem.Setups;
1314
using Moryx.Logging;
@@ -30,10 +31,9 @@ internal class SetupJobHandler : ILoggingComponent, ISetupJobHandler
3031
public IJobDataList JobList { get; set; }
3132

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

3838
/// <summary>
3939
/// Temporary recipe provider
@@ -92,7 +92,7 @@ private LinkedListNode<IJobData> HandleCurrentJob(LinkedList<IJobData> newJobs,
9292
SetupRecipe prepareRecipe;
9393
try
9494
{
95-
prepareRecipe = SetupProvider?.RequiredSetup(SetupExecution.BeforeProduction, productionJob.Recipe, new CurrentResourceTarget(ResourceManagement));
95+
prepareRecipe = SetupManager.RequiredSetup(SetupExecution.BeforeProduction, productionJob.Recipe, new CurrentResourceTarget(ResourceManagement));
9696
}
9797
catch (Exception setupProviderException)
9898
{
@@ -112,7 +112,7 @@ private LinkedListNode<IJobData> HandleCurrentJob(LinkedList<IJobData> newJobs,
112112
try
113113
{
114114
// Clean-up is evaluated just in time, so we only create a temporary recipe
115-
cleanupRecipe = SetupProvider?.RequiredSetup(SetupExecution.AfterProduction, productionJob.Recipe, new TemporaryCleanupTarget()) ??
115+
cleanupRecipe = SetupManager.RequiredSetup(SetupExecution.AfterProduction, productionJob.Recipe, new TemporaryCleanupTarget()) ??
116116
RecipeProvider.CreateTemporary(productionJob.Recipe);
117117
}
118118
catch (Exception e)
@@ -204,7 +204,7 @@ private bool LastOfRecipe(LinkedList<IJobData> newJobs, IJobData currentNode)
204204
private void LogAbortedJobs(Exception e, List<long> abortedJobIds, SetupExecution executionType)
205205
{
206206
Logger.LogError(e, "{provider} threw an exception when creating required setup {classification} for " +
207-
"job(s) {jobs}. Interrupting job(s)...", SetupProvider.GetType().Name, executionType, string.Join(", ", abortedJobIds));
207+
"job(s) {jobs}. Interrupting job(s)...", SetupManager.GetType().Name, executionType, string.Join(", ", abortedJobIds));
208208
}
209209

210210
/// <summary>
@@ -282,7 +282,7 @@ private void CheckRecipeUpdate(IJobData jobData)
282282

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

src/Moryx.ControlSystem.ProcessEngine/ModuleController/ComponentOrchestration.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Moryx.ControlSystem.ProcessEngine.Jobs;
88
using Moryx.ControlSystem.ProcessEngine.Jobs.Setup;
99
using Moryx.ControlSystem.ProcessEngine.Processes;
10+
using Moryx.ControlSystem.ProcessEngine.Setups;
1011
using Moryx.Logging;
1112

1213
namespace Moryx.ControlSystem.ProcessEngine
@@ -37,19 +38,23 @@ internal class ComponentOrchestration
3738

3839
public IActivityPoolListener[] PoolListeners { get; set; }
3940

40-
public ISetupJobHandler SetupManager { get; set; }
41+
public ISetupJobHandler SetupJobHandler { get; set; }
42+
43+
public ISetupManager SetupManager { get; set; }
4144

4245
public IModuleLogger Logger { get; set; }
4346

4447
#endregion
4548

4649
public void Start()
4750
{
51+
SetupManager.Start();
52+
4853
JobStorage.Start();
4954

5055
JobScheduler.Initialize(Config.JobSchedulerConfig);
5156

52-
SetupManager.Start();
57+
SetupJobHandler.Start();
5358

5459
ProcessStorage.Start();
5560

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

88-
SetupManager.Stop();
93+
SetupJobHandler.Stop();
8994
JobDispatcher.Stop();
9095

9196
JobManager.Stop();
@@ -100,6 +105,8 @@ public void Stop()
100105
JobList.Stop();
101106

102107
JobStorage.Stop();
108+
109+
SetupManager.Stop();
103110
}
104111
}
105112
}

src/Moryx.ControlSystem.ProcessEngine/ModuleController/ModuleConfig.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Moryx.ControlSystem.Cells;
88
using Moryx.ControlSystem.Jobs;
99
using Moryx.ControlSystem.ProcessEngine.Jobs;
10+
using Moryx.ControlSystem.Setups;
1011
using Moryx.Notifications;
1112
using Moryx.Runtime.Configuration;
1213
using Moryx.Serialization;
@@ -117,6 +118,12 @@ public ModuleConfig()
117118
[DataMember, DefaultValue(DefaultSetupJobRetryLimit)]
118119
[Description("Limit of retries for a failed setup job. Smaller than 0 for infinite.")]
119120
public int SetupJobRetryLimit { get; set; }
121+
122+
/// <summary>
123+
/// All configured setup triggers for this application
124+
/// </summary>
125+
[DataMember, PluginConfigs(typeof(ISetupTrigger))]
126+
public List<SetupTriggerConfig> SetupTriggers { get; set; }
120127
}
121128

122129
/// <summary>

src/Moryx.ControlSystem.ProcessEngine/ModuleController/ModuleController.cs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,15 @@ namespace Moryx.ControlSystem.ProcessEngine
2424
"Job execution is influenced by a jobs constraint and evaluated by dedicated components of the DefaultJobScheduler. " +
2525
"Jobs may include but are not limited to production, maintenance and resource configuration.")]
2626
public class ModuleController : ServerModuleBase<ModuleConfig>,
27-
IFacadeContainer<IJobManagement>, IFacadeContainer<INotificationSource>, IFacadeContainer<IProcessControl>
27+
IFacadeContainer<IJobManagement>,
28+
IFacadeContainer<INotificationSource>,
29+
IFacadeContainer<IProcessControl>,
30+
IFacadeContainer<ISetupProvider>
2831
{
2932
/// <summary>
3033
/// The module's name.
3134
/// </summary>
32-
public const string ModuleName = "ProcessEngine";
35+
private const string ModuleName = "ProcessEngine";
3336

3437
/// <inheritdoc />
3538
public override string Name => ModuleName;
@@ -55,13 +58,7 @@ public ModuleController(IModuleContainerFactory containerFactory, IConfigManager
5558
/// </summary>
5659
[RequiredModuleApi(IsStartDependency = true, IsOptional = false)]
5760
public IResourceManagement ResourceManagement { get; set; }
58-
59-
/// <summary>
60-
/// Found recipe providers used to restore recipes links of jobs and processes
61-
/// </summary>
62-
[RequiredModuleApi(IsStartDependency = true, IsOptional = true)]
63-
public ISetupProvider SetupProvider { get; set; }
64-
61+
6562
/// <summary>
6663
/// Product management to load and save articles instances and products
6764
/// </summary>
@@ -85,9 +82,9 @@ protected override Task OnInitializeAsync(CancellationToken cancellationToken)
8582
// Register all imported components
8683
Container.SetInstance(ResourceManagement)
8784
.SetInstance(ProductManagement);
88-
// Register optional setup dependency
89-
if (SetupProvider != null)
90-
Container.SetInstance(SetupProvider);
85+
86+
// Register plugins for the setup management
87+
Container.LoadComponents<ISetupTrigger>();
9188

9289
// Register process plugins
9390
Container.LoadComponents<ICellSelector>();
@@ -105,6 +102,7 @@ protected override Task OnInitializeAsync(CancellationToken cancellationToken)
105102
protected override Task OnStartAsync(CancellationToken cancellationToken)
106103
{
107104
// Activate facade
105+
ActivateFacade(_setupProviderFacade);
108106
ActivateFacade(_jobManagementFacade);
109107
ActivateFacade(_notificationSourceFacade);
110108
ActivateFacade(_processControlFacade);
@@ -127,6 +125,7 @@ protected override Task OnStopAsync(CancellationToken cancellationToken)
127125
DeactivateFacade(_jobManagementFacade);
128126
DeactivateFacade(_notificationSourceFacade);
129127
DeactivateFacade(_processControlFacade);
128+
DeactivateFacade(_setupProviderFacade);
130129

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

146145
IProcessControl IFacadeContainer<IProcessControl>.Facade => _processControlFacade;
147146

147+
private readonly SetupProviderFacade _setupProviderFacade = new();
148+
149+
ISetupProvider IFacadeContainer<ISetupProvider>.Facade => _setupProviderFacade;
150+
148151
#endregion
149152
}
150153
}

src/Moryx.ControlSystem.SetupProvider/Components/ISetupManager.cs renamed to src/Moryx.ControlSystem.ProcessEngine/Setups/ISetupManager.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66
using Moryx.ControlSystem.Setups;
77
using Moryx.Modules;
88

9-
namespace Moryx.ControlSystem.SetupProvider
9+
namespace Moryx.ControlSystem.ProcessEngine.Setups
1010
{
11-
internal interface ISetupManager : IPlugin
11+
/// <summary>
12+
/// Component that can determine setup requirement and create setup recipes
13+
/// </summary>
14+
internal interface ISetupManager : IPlugin, IRecipeProvider
1215
{
1316
SetupRecipe RequiredSetup(SetupExecution execution, ProductionRecipe recipe, ISetupTarget targetSystem);
1417
}

0 commit comments

Comments
 (0)