generated from PHOENIXCONTACT/MORYX-Template
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/dev'
- Loading branch information
Showing
27 changed files
with
777 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
6.1.1 | ||
6.2.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
# Simulation Driver | ||
|
||
If none of the existing, commercial drivers from `Moryx.Drivers.Simulation` fits your requirements, you can easily implement your own driver. All you need to do is implement the interface `Moryx.Simulation.ISimulationDriver` as well as the interface of the driver you want to replace like `IMessageDriver<object>` or `IInOutDriver`. | ||
|
||
A module like the commercial simulator will call `Ready` and `Result` on your instance as a replacement for the events usually received from real drivers and sensors. You need to translate them into values or events of you driver interface like `IMessageDriver.Received`. The methods references the activity which you can use to extract the relevant attributes like process id, carrier or product identifiers as needed by your cell. Process execution initiated by the drivers API like parameter values or `Send` methods need to be represented by the `SimulatedState`. | ||
|
||
## Example of simulated driver | ||
|
||
So here is an example of how your mock driver can implement `Moryx.Simulation.ISimulationDriver`: | ||
``` csharp | ||
[ResourceRegistration] | ||
public class TestMockDriver : Driver, IMessageDriver<object>,ISimulationDriver { | ||
|
||
public bool HasChannels => false; | ||
|
||
public IDriver Driver => this; | ||
|
||
public string Identifier => Name; | ||
|
||
private SimulationState _simulatedState; | ||
// simulated state of the driver | ||
public SimulationState SimulatedState | ||
{ | ||
get => _simulatedState; | ||
private set | ||
{ | ||
_simulatedState = value; | ||
SimulatedStateChanged?.Invoke(this, value); | ||
} | ||
} | ||
|
||
// cell referenced by the driver | ||
[ResourceReference(ResourceRelationType.Driver, ResourceReferenceRole.Source)] | ||
public AssemblyCell Cell { get; set; } | ||
|
||
public IEnumerable<ICell> Usages => new[] { Cell }; | ||
|
||
protected override void OnStart() | ||
{ | ||
base.OnStart(); | ||
// initial simulated state of the driver | ||
SimulatedState = SimulationState.Idle; | ||
} | ||
|
||
public IMessageChannel<TChannel> Channel<TChannel>(string identifier) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
|
||
public IMessageChannel<TSend, TReceive> Channel<TSend, TReceive>(string identifier) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
|
||
// Message received from the cell | ||
public void Send(object payload) | ||
{ | ||
switch (payload) | ||
{ | ||
case AssembleProductMessage assemble: | ||
SimulatedState = SimulationState.Executing; | ||
break; | ||
case ReleaseWorkpieceMessage release: | ||
SimulatedState = SimulationState.Idle; | ||
break; | ||
} | ||
} | ||
|
||
public Task SendAsync(object payload) | ||
{ | ||
Send(payload); | ||
return Task.CompletedTask; | ||
} | ||
|
||
public void Ready(IActivity activity) | ||
{ | ||
SimulatedState = SimulationState.Requested; | ||
|
||
Received?.Invoke(this, new WorkpieceArrivedMessage { ProcessId = activity.Process.Id }); | ||
} | ||
|
||
public void Result(SimulationResult result) | ||
{ | ||
Received?.Invoke(this, new AssemblyCompletedMessage { Result = result.Result }); | ||
} | ||
|
||
public event EventHandler<object> Received; | ||
|
||
public event EventHandler<SimulationState> SimulatedStateChanged; | ||
|
||
//... rest of your codes// | ||
} | ||
``` | ||
|
||
Note: The `Send()` method might differ from cell to cell.You can override the `Send()` and do your implementation of what needs to happend when your "mock" driver receives something from the cell. In case your driver doesn't have a `send()` method, you can use your own method and react upon the type of message you received just like described inside the `TestMockDriver.Send()` method above. | ||
|
||
# How does my Cell use my `MockDriver` ? | ||
|
||
In your cell definition/Class you can use the following example based on the type of driver you're trying to simulate. Let's say we are trying to "mock" an `MQTT driver` since Moryx MQTT driver implements from `IMessageDriver<object>` in your cell you can have the following case: | ||
```csharp | ||
// SolderingCell.cs | ||
[ResourceRegistration] | ||
public class SolderingCell : Cell | ||
{ | ||
//... rest of your codes// | ||
[ResourceReference(ResourceRelationType.Driver, IsRequired = true)] | ||
public IMessageDriver<object> Driver { get; set; } | ||
|
||
//... rest of your codes// | ||
} | ||
``` | ||
|
||
Since the our "mock" driver has the following property defined: | ||
```csharp | ||
//... rest of the codes// | ||
/// <summary> | ||
/// Cell linked to the driver | ||
/// </summary> | ||
[ResourceReference(ResourceRelationType.Driver, ResourceReferenceRole.Source)] | ||
public AssemblyCell Cell { get; set; } | ||
|
||
//... rest of the codes// | ||
``` | ||
Moryx will take care of the relationship once you assign the driver to the cell. | ||
To start the communication you can define message types in your resources folder. For starter we can define some message types. You can also use already existing messages | ||
```csharp | ||
//AssembleProductMessage.cs | ||
/// <summary> | ||
/// Message specificto start an activity | ||
/// </summary> | ||
public class AssembleProductMessage | ||
{ | ||
public long ActivityId { get; set; } | ||
} | ||
|
||
//AssemblyCompletedMessage.cs | ||
public class AssemblyCompletedMessage | ||
{ | ||
public int Result { get; set; } | ||
} | ||
|
||
//ReleaseWorkpieceMessage.cs | ||
public class ReleaseWorkpieceMessage | ||
{ | ||
//rest of the code... | ||
} | ||
|
||
//WorkpieceArrivedMessage.cs | ||
public class WorkpieceArrivedMessage | ||
{ | ||
public long ProcessId { get; set; } | ||
} | ||
``` | ||
In the cell when you need to send a data to the driver you can do as described bellow: | ||
```csharp | ||
//AssemblyCell.cs | ||
//... rest of the codes// | ||
public override void StartActivity(ActivityStart activityStart) | ||
{ | ||
Driver.Send(new AssembleProductMessage { ActivityId = activityStart.Activity.Id }); | ||
} | ||
|
||
//... rest of the codes// | ||
``` | ||
As you can see inside the `StartActivity` method we want the cell to send data to the driver. | ||
|
||
Now in our driver `TestMockDriver` class above. Inside the `public void Send(object payload)` method we are filtering the payload based on the message type that we just define. We can act upon the type of message received. For instance: | ||
```csharp | ||
//... rest of the codes// | ||
public void Send(object payload) | ||
{ | ||
switch (payload) | ||
{ | ||
case AssembleProductMessage assemble: | ||
SimulatedState = SimulationState.Executing; | ||
break; | ||
case ReleaseWorkpieceMessage release: | ||
SimulatedState = SimulationState.Idle; | ||
break; | ||
} | ||
} | ||
|
||
//... rest of the codes// | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# Simulation parameters | ||
|
||
If you need to influence the simulation behavior of activites you can add `ISimulationParameters` to your parameters and return different execution times either by configuration or through binding on digital twins. | ||
|
||
## Configurable execution time | ||
|
||
The example below shows how to implement configurable execution time parameters | ||
|
||
````cs | ||
internal class ConfiguredSimulationParameter : Parameters, ISimulationParameters | ||
{ | ||
[EntrySerialize(EntrySerializeMode.Always)] | ||
[Description("Execution time in seconds")] | ||
public int ExecutionTimeSec { get; set; } | ||
|
||
TimeSpan ISimulationParameters.ExecutionTime => new TimeSpan(0, 0, ExecutionTimeSec); | ||
|
||
/// <inheritdoc /> | ||
protected override void Populate(IProcess process, Parameters instance) | ||
{ | ||
var parameters = (ConfiguredSimulationParameter)instance; | ||
|
||
parameters.ExecutionTimeSec = ExecutionTimeSec; | ||
} | ||
} | ||
```` | ||
|
||
## Binding execution times | ||
|
||
You can also determine execution time by binding like any other parameters. You could bind to static values or calculate execution time dynamically. | ||
|
||
````cs | ||
internal class BindingSimulationParameters : Parameters, ISimulationParameters | ||
{ | ||
[EntrySerialize(EntrySerializeMode.Never)] | ||
public TimeSpan ExecutionTime { get; set; } | ||
|
||
/// <inheritdoc /> | ||
protected override void Populate(IProcess process, Parameters instance) | ||
{ | ||
var parameters = (BindingSimulationParameters)instance; | ||
|
||
var recipe = (MyRecipe)process.Recipe; | ||
parameters.ExecutionTime = new TimeSpan(recipe.TestTimeSec); | ||
} | ||
} | ||
```` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// Copyright (c) 2023, Phoenix Contact GmbH & Co. KG | ||
// Licensed under the Apache License, Version 2.0 | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Text; | ||
|
||
namespace Moryx.Factory | ||
{ | ||
/// <summary> | ||
/// Attribute for a visual representation of the current property inside the Factory monitor UI | ||
/// </summary> | ||
public class EntryVisualizationAttribute : Attribute | ||
{ | ||
public EntryVisualizationAttribute(string unit, string icon) | ||
{ | ||
Unit = unit; | ||
Icon = icon; | ||
} | ||
|
||
/// <summary> | ||
/// Unit of the value for the current property (Ex. Kw/h) | ||
/// </summary> | ||
public string Unit { get; } | ||
|
||
/// <summary> | ||
/// Icon to display for this property inside the Factory Monitor UI | ||
/// </summary> | ||
public string Icon { get; } | ||
} | ||
} |
Oops, something went wrong.