Skip to content
Merged
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
2 changes: 1 addition & 1 deletion MORYX-Framework.sln
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DFC092A6-B935-4D19-A564-9AEDEEF999B9}"
ProjectSection(SolutionItems) = preProject
.build\Common.props = .build\Common.props
Directory.build.props = Directory.build.props
Directory.Build.props = Directory.Build.props
Directory.Build.targets = Directory.Build.targets
Directory.Packages.props = Directory.Packages.props
LICENSE = LICENSE
Expand Down
3 changes: 3 additions & 0 deletions docs/articles/ControlSystem.eap
Git LFS file not shown
4 changes: 2 additions & 2 deletions docs/articles/module-orders/images/OperationsStates.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 6 additions & 8 deletions docs/articles/module-orders/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ This modules exports the `IOrderManagement` facade.

## Structure

![Components](images\OrderManagementComponents.png)
![Components](images/OrderManagementComponents.png)

The internal structure of the OrderManagement is shown in the component diagram. On the top is the OrderWcf which uses the OperationPool to get any update of each `OperationData` or perform some operations on each one. The `OperationPool` is an implementation of the Object Pooling Pattern which holds all known OperationData. The `OperationData` is the main component which handles all operation related functionalities. There is the `OperationAssignment` to assign all needed Information to a new or reloaded OperationData to make it producible. The `JobHandler` will be used to communicate with the `JobManagement` of the ProcessEngine to dispatch Jobs for the production.

Expand All @@ -62,7 +62,7 @@ The [IOperationPool](xref:Moryx.Orders.IOperationPool) is used for all module pl

### State Machine

![OperationData State Machine](images\OperationsStates.png)
![OperationData State Machine](images/OperationsStates.png)

#### Initial

Expand All @@ -86,7 +86,7 @@ The OperationData is still running in this state until the User decides to start

#### Interrupted

The OperationData can be Interrupted if the it is in the RunningState or AmountReachedState (both of the states have the state classification [Running](xref:Moryx.Orders.OperationClassification)). In both case all jobs will be completed and the worker can add some report information which is a partial report. The OperationData switches from the RunningState to the InterruptingState first to wait until all jobs are completed. After all jobs are completed the state switches to the InterruptedState which leads to a Interrupted event with the user from the last partial report. In the AmountReachedState all jobs are already completed so there is no reason to wait for some jobs.
The OperationData can be Interrupted if the it is in the RunningState or AmountReachedState (both of the states have the state classification [Running](xref:Moryx.Orders.OperationClassification)). In both case all jobs will be completed. The OperationData switches from the RunningState to the InterruptingState first to wait until all jobs are completed. After all jobs are completed the state switches to the InterruptedState which leads to a Interrupted event. In the AmountReachedState all jobs are already completed so there is no reason to wait for some jobs.

#### Completed

Expand Down Expand Up @@ -134,7 +134,7 @@ In all cases a reporting will be performed. The reporting information are encaps
- Comment: Optional information
- UserId: The id of the user which was selected to document who has done the report

Each report is depending to the machine state and only possible if the state has the classification `Production`. It is possible that there is are no states or no production state. In this case the reports are independent to the machine state. A state change to the production state will lead to a state change task followed by a report task if there is something to report. If the state switches from the production state to a non production state then this will lead to a report task followed by a state change task to ensure that everything will be reported in the production state.
Each report is depending to the machine state and only possible if the state has the classification `Production`. It is possible that there is are no states or no production state. In this case the reports are independent to the machine state. A state change to the production state will lead to a state change task followed by a report task if there is something to report. If the state switches from the production state to a non production state then this will lead to a report task followed by a state change task to ensure that everything will be reported in the production state.

### Advice PickParts/Order

Expand Down Expand Up @@ -196,8 +196,6 @@ For example there are two failure parts and 5 reworked. So the user spends only
#### Interrupt Behavior

An interrupt can be performed in the `AmountReachedState` and in the `RunningState`. The main difference is that in the `RunningState` there are running jobs which are still producing.
So if the worker decides to interrupt the OperationData during the `RunningState` then he only gets the current `SuccessCount` and `ScrapCount`. The parts which are not done are not taken into account for the possible reporting and will be finished afterwards and can be reported later.
An interrupt is predefined with the `ConfirmationType` [Partial](@ref Moryx.Orders.ConfirmationType). So only a partial report is possible.

#### Restore Behavior

Expand Down Expand Up @@ -290,7 +288,7 @@ If the OrderManagement will be restarted, it is necessary to reload the jobs whi
The ui of the order management will be used to show all orders to produce on the current machine. It is also possible to create order without an external system like HYDRA or SAP.

**Concept**
The orders ui contains a list of orders for a first overview. The details of each order can be displayed by selecting it.
The orders ui contains a list of orders for a first overview. The details of each order can be displayed by selecting it.
The ui has an area for all order details. This contains general information like order number and the due date, information about the product to produce and a list of operations of the selected order.

**Hide completed**
Expand All @@ -309,7 +307,7 @@ The job UI provides also the possibility to start, abort and reorder each job.

**Concept**
The job UI is basically a list of all current jobs. There are some buttons on the bottom side to start, abort and moving each job.
To start, abort or moving a job it is necessary to select the job and click on the button you want.
To start, abort or moving a job it is necessary to select the job and click on the button you want.
The buttons will be automatically disabled and enabled depending if the action is executable with the selected job.


4 changes: 4 additions & 0 deletions docs/migrations/v8_to_v10.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ These feature were infrequently used and has been removed to simplify the codeba
- Moryx.Tools.FunctionResult: Moved to Moryx.Tools
- Moryx.AbstractionLayer namespace: All classes have been moved to more specific domain namespaces e.g. Moryx.AbstractionLayer.Resources, Moryx.AbstractionLayer.Products, Moryx.AbstractionLayer.Processes etc.

## Modules-Orders

- Removed report from interrupt of an operation. Reporting during an interruption doesn't add any value. The quantity for the report can only be predicted and will be inaccurate if something goes wrong or is reworked during the interruption.

## Reworked driver APIs

All driver APIs have been reworked to use TPL async/await instead of callbacks for the following reasons:
Expand Down
29 changes: 13 additions & 16 deletions src/Moryx.Orders.Endpoints/OrderManagementController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public async Task OperationStream(CancellationToken cancelToken)
var updateEventHandler = new EventHandler<OperationChangedEventArgs>((_, eventArgs) =>
{
var json = JsonConvert.SerializeObject(Converter.ToModel(eventArgs.Operation), _serializerSettings);
operationsChannel.Writer.TryWrite(new Tuple<string, string>(OperationTypes.Update.ToString(), json));
operationsChannel.Writer.TryWrite(new Tuple<string, string>(nameof(OperationTypes.Update), json));
});
_orderManagement.OperationUpdated += updateEventHandler;

Expand All @@ -80,7 +80,7 @@ public async Task OperationStream(CancellationToken cancelToken)
Advice = Converter.ToModel(eventArgs.Advice)
};
var json = JsonConvert.SerializeObject(advicedOperation, _serializerSettings);
operationsChannel.Writer.TryWrite(new Tuple<string, string>(OperationTypes.Advice.ToString(), json));
operationsChannel.Writer.TryWrite(new Tuple<string, string>(nameof(OperationTypes.Advice), json));
});
_orderManagement.OperationAdviced += adviceEventHandler;

Expand All @@ -92,19 +92,14 @@ public async Task OperationStream(CancellationToken cancelToken)
Report = Converter.ToModel(eventArgs.Report)
};
var json = JsonConvert.SerializeObject(reportedOperation, _serializerSettings);
operationsChannel.Writer.TryWrite(new Tuple<string, string>(OperationTypes.Report.ToString(), json));
operationsChannel.Writer.TryWrite(new Tuple<string, string>(nameof(OperationTypes.Report), json));
});
_orderManagement.OperationPartialReport += reportEventHandler;

var interruptedEventHandler = new EventHandler<OperationReportEventArgs>((_, eventArgs) =>
var interruptedEventHandler = new EventHandler<OperationChangedEventArgs>((_, eventArgs) =>
{
var interruptedOperation = new OperationReportedModel
{
OperationModel = Converter.ToModel(eventArgs.Operation),
Report = Converter.ToModel(eventArgs.Report)
};
var json = JsonConvert.SerializeObject(interruptedOperation, _serializerSettings);
operationsChannel.Writer.TryWrite(new Tuple<string, string>(OperationTypes.Interrupted.ToString(), json));
var json = JsonConvert.SerializeObject(Converter.ToModel(eventArgs.Operation), _serializerSettings);
operationsChannel.Writer.TryWrite(new Tuple<string, string>(nameof(OperationTypes.Interrupted), json));
});
_orderManagement.OperationInterrupted += interruptedEventHandler;

Expand All @@ -116,7 +111,7 @@ public async Task OperationStream(CancellationToken cancelToken)
Report = Converter.ToModel(eventArgs.Report)
};
var json = JsonConvert.SerializeObject(completedOperation, _serializerSettings);
operationsChannel.Writer.TryWrite(new Tuple<string, string>(OperationTypes.Completed.ToString(), json));
operationsChannel.Writer.TryWrite(new Tuple<string, string>(nameof(OperationTypes.Completed), json));
});
_orderManagement.OperationCompleted += completedEventHandler;

Expand All @@ -128,14 +123,14 @@ public async Task OperationStream(CancellationToken cancelToken)
UserId = eventArgs.User.Identifier
};
var json = JsonConvert.SerializeObject(startedOperation, _serializerSettings);
operationsChannel.Writer.TryWrite(new Tuple<string, string>(OperationTypes.Start.ToString(), json));
operationsChannel.Writer.TryWrite(new Tuple<string, string>(nameof(OperationTypes.Start), json));
});
_orderManagement.OperationStarted += startedEventHandler;

var changedEventHandler = new EventHandler<OperationChangedEventArgs>((_, eventArgs) =>
{
var json = JsonConvert.SerializeObject(Converter.ToModel(eventArgs.Operation), _serializerSettings);
operationsChannel.Writer.TryWrite(new Tuple<string, string>(OperationTypes.Progress.ToString(), json));
operationsChannel.Writer.TryWrite(new Tuple<string, string>(nameof(OperationTypes.Progress), json));
});
_orderManagement.OperationProgressChanged += changedEventHandler;

Expand Down Expand Up @@ -424,13 +419,15 @@ public ActionResult ReportOperation(Guid guid, ReportModel report)
[ProducesResponseType(typeof(MoryxExceptionResponse), StatusCodes.Status404NotFound)]
[Route("{guid}/interrupt")]
[Authorize(Policy = OrderPermissions.CanInterrupt)]
public ActionResult InterruptOperation(Guid guid, ReportModel report)
public ActionResult InterruptOperation(Guid guid, string userIdentifier)
{
var operation = _orderManagement.GetOperation(guid);
if (operation == null)
return NotFound(new MoryxExceptionResponse { Title = Strings.OrderManagementController_OperationNotFound });

_orderManagement.InterruptOperation(operation, Converter.FromModel(report, _userManagement));
var user = _userManagement?.GetUser(userIdentifier);

_orderManagement.InterruptOperation(operation, user);
return Ok();
}

Expand Down
5 changes: 3 additions & 2 deletions src/Moryx.Orders.Management/Components/IOperationData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ internal interface IOperationData
/// <summary>
/// Disables an operation which leads to complete all jobs
/// </summary>
void Interrupt(OperationReport report);
/// <param name="user"></param>
void Interrupt(User user);

/// <summary>
/// Will do a report for the operation.
Expand Down Expand Up @@ -195,7 +196,7 @@ internal interface IOperationData
/// <summary>
/// Raised if the operation was interrupted
/// </summary>
event EventHandler<ReportEventArgs> Interrupted;
event EventHandler<OperationEventArgs> Interrupted;

/// <summary>
/// Raised if the operation was completed
Expand Down
14 changes: 7 additions & 7 deletions src/Moryx.Orders.Management/Facade/OrderManagementFacade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ private void OnOperationStarted(object sender, StartedEventArgs e)
OperationStarted?.Invoke(this, new OperationStartedEventArgs(e.OperationData.Operation, e.User));
}

private void OnOperationInterrupted(object sender, ReportEventArgs e)
private void OnOperationInterrupted(object sender, OperationEventArgs e)
{
OperationInterrupted?.Invoke(this, new OperationReportEventArgs(e.OperationData.Operation, e.Report));
OperationInterrupted?.Invoke(this, new OperationChangedEventArgs(e.OperationData.Operation));
}

private void OnOperationCompleted(object sender, ReportEventArgs e)
Expand Down Expand Up @@ -257,15 +257,15 @@ public ReportContext GetInterruptContext(Operation operation)
return OperationManager.GetInterruptContext(operationData);
}

public void InterruptOperation(Operation operation, OperationReport report)
public void InterruptOperation(Operation operation, User user)
{
ValidateHealthState();

// Get default user if there is no in the report
report.User ??= UserManagement.DefaultUser;
// Get default user if there is no
user ??= UserManagement.DefaultUser;

var operationData = GetOperationDataSave(operation);
OperationManager.Interrupt(operationData, report);
OperationManager.Interrupt(operationData, user);
}

private IOperationData GetOperationDataSave(Operation operation)
Expand Down Expand Up @@ -332,7 +332,7 @@ public Task<IReadOnlyList<IProductRecipe>> GetAssignableRecipes(ProductIdentity

public event EventHandler<OperationReportEventArgs> OperationCompleted;

public event EventHandler<OperationReportEventArgs> OperationInterrupted;
public event EventHandler<OperationChangedEventArgs> OperationInterrupted;

public event EventHandler<OperationReportEventArgs> OperationPartialReport;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -420,42 +420,35 @@ internal void HandleStarted(User user)
Started?.Invoke(this, new StartedEventArgs(this, user));
}

/// <param name="user"></param>
/// <inheritdoc cref="IOperationData"/>
public void Interrupt(OperationReport report)
public void Interrupt(User user)
{
Log(LogLevel.Debug, "Operation will be interrupted with SuccessCount {0} and FailureCount {1} by user {2}",
report.SuccessCount, report.FailureCount, report.User.Identifier);
Log(LogLevel.Debug, "Operation will be interrupted by user {0}",
user.Identifier);

lock (_stateLock)
_state.Interrupt(report);
_state.Interrupt(user);
}

/// <summary>
/// Will complete all jobs and executes a partial report
/// </summary>
internal void HandleManualInterrupting(OperationReport report)
internal void HandleManualInterrupting()
{
JobHandler.Complete(this);
HandlePartialReport(report);
}

/// <summary>
/// Will handle manual interrupts. The interrupt was triggered by the user.
/// Will throw the <see cref="Interrupted"/> event
/// </summary>
internal void HandleManualInterrupted(OperationReport report)
internal void HandleManualInterrupted()
{
Operation.TargetAmount = ReachableAmount;

lock (_reports)
{
_reports.Add(report);
Operation.Reports = _reports.ToArray();

}

Updated?.Invoke(this, new OperationEventArgs(this));
Interrupted?.Invoke(this, new ReportEventArgs(this, report));
Interrupted?.Invoke(this, new OperationEventArgs(this));
}

/// <summary>
Expand All @@ -466,19 +459,8 @@ internal void HandleInterrupted()
{
Operation.TargetAmount = ReachableAmount;

// Add report for interrupted with user which have started the interruption
OperationReport report;
lock (_reports)
{
var lastReportUser = Operation.Reports.Last().User;
report = new OperationReport(ConfirmationType.Partial, 0, 0, lastReportUser);

_reports.Add(report);
Operation.Reports = _reports.ToArray();
}

Updated?.Invoke(this, new OperationEventArgs(this));
Interrupted?.Invoke(this, new ReportEventArgs(this, report));
Interrupted?.Invoke(this, new OperationEventArgs(this));
}

/// <inheritdoc cref="IOperationData"/>
Expand Down Expand Up @@ -757,7 +739,7 @@ private TInfo GetOperationInfo<TInfo>()
public event EventHandler<StartedEventArgs> Started;

/// <inheritdoc cref="IOperationData.Interrupted"/>
public event EventHandler<ReportEventArgs> Interrupted;
public event EventHandler<OperationEventArgs> Interrupted;

/// <inheritdoc cref="IOperationData.Completed"/>
public event EventHandler<ReportEventArgs> Completed;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,21 +54,10 @@ public override void IncreaseTargetBy(int amount, User user)
Context.HandleIncreaseTargetBy(amount);
}

public override void Interrupt(OperationReport report)
public override void Interrupt(User user)
{
switch (report.ConfirmationType)
{
case ConfirmationType.Final:
NextState(StateCompleted);
Context.HandleCompleted(report);
break;
case ConfirmationType.Partial:
NextState(StateInterrupted);
Context.HandleManualInterrupted(report);
break;
default:
throw new ArgumentOutOfRangeException(nameof(report.ConfirmationType), report.ConfirmationType, null);
}
NextState(StateInterrupted);
Context.HandleManualInterrupted();
}

public override ReportContext GetReportContext()
Expand Down
Loading
Loading