Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
Toxantron committed Aug 22, 2023
2 parents 08412a5 + dbdfa11 commit c03fc53
Show file tree
Hide file tree
Showing 27 changed files with 777 additions and 10 deletions.
3 changes: 2 additions & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ version: 2
updates:
- package-ecosystem: "nuget" # See documentation for possible values
directory: "/" # Location of package manifests
target-branch: "future"
open-pull-requests-limit: 10
schedule:
interval: "weekly"
Expand All @@ -19,4 +20,4 @@ updates:
- "nuget"
- "dependencies"
ignore:
- dependency-name: "Moryx.AbstractionLayer"
- dependency-name: "Moryx.AbstractionLayer"
8 changes: 4 additions & 4 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@

<!-- Package refereces for all projects if CreatePackage=true -->
<ItemGroup Condition="'$(CreatePackage)' == 'true'">
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
</ItemGroup>

<!-- Package versions for package references across all projects -->
<ItemGroup>
<!--3rd party dependencies-->
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Update="Moq" Version="4.17.2" />
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="17.6.3" />
<PackageReference Update="Moq" Version="4.20.69" />
<PackageReference Update="NUnit" Version="3.13.3" />
<PackageReference Update="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Update="NUnit3TestAdapter" Version="4.5.0" />
<PackageReference Update="coverlet.collector" Version="3.2.0" >
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
16 changes: 14 additions & 2 deletions MoryxFactory.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29215.179
# Visual Studio Version 17
VisualStudioVersion = 17.4.33213.308
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Moryx.ControlSystem", "src\Moryx.ControlSystem\Moryx.ControlSystem.csproj", "{DF4730D4-5DB2-46A5-9F2B-C7B22E61268B}"
EndProject
Expand All @@ -28,6 +28,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Moryx.ProcessData", "src\Mo
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Moryx.ProcessData.Tests", "src\Tests\Moryx.ProcessData.Tests\Moryx.ProcessData.Tests.csproj", "{8E9609B6-9AF6-4F8E-A8DD-0B98E8B3B587}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moryx.Factory", "src\Moryx.Factory\Moryx.Factory.csproj", "{4B1C8062-EF37-4880-AFDF-2C3CCEE3A848}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Moryx.Simulation", "src\Moryx.Simulation\Moryx.Simulation.csproj", "{23B4A0C7-6F13-4FBB-998C-CD56DCF6D775}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -58,6 +62,14 @@ Global
{8E9609B6-9AF6-4F8E-A8DD-0B98E8B3B587}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8E9609B6-9AF6-4F8E-A8DD-0B98E8B3B587}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8E9609B6-9AF6-4F8E-A8DD-0B98E8B3B587}.Release|Any CPU.Build.0 = Release|Any CPU
{4B1C8062-EF37-4880-AFDF-2C3CCEE3A848}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4B1C8062-EF37-4880-AFDF-2C3CCEE3A848}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4B1C8062-EF37-4880-AFDF-2C3CCEE3A848}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4B1C8062-EF37-4880-AFDF-2C3CCEE3A848}.Release|Any CPU.Build.0 = Release|Any CPU
{23B4A0C7-6F13-4FBB-998C-CD56DCF6D775}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{23B4A0C7-6F13-4FBB-998C-CD56DCF6D775}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23B4A0C7-6F13-4FBB-998C-CD56DCF6D775}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23B4A0C7-6F13-4FBB-998C-CD56DCF6D775}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ This repository contains the APIs, domain objects and developer documentation fo
| `Moryx.Orders` | [![NuGet](https://img.shields.io/nuget/v/Moryx.Orders.svg)](https://www.nuget.org/packages/Moryx.Orders/) | [![MyGet](https://img.shields.io/myget/moryx/vpre/Moryx.Orders)](https://www.myget.org/feed/moryx/package/nuget/Moryx.Orders) | [![MyGet-Release](https://img.shields.io/myget/moryx-future/vpre/Moryx.Orders)](https://www.myget.org/feed/moryx-future/package/nuget/Moryx.Orders) |
| `Moryx.Users` | [![NuGet](https://img.shields.io/nuget/v/Moryx.Users.svg)](https://www.nuget.org/packages/Moryx.Users/) | [![MyGet](https://img.shields.io/myget/moryx/vpre/Moryx.Users)](https://www.myget.org/feed/moryx/package/nuget/Moryx.Users) | [![MyGet-Release](https://img.shields.io/myget/moryx-future/vpre/Moryx.Users)](https://www.myget.org/feed/moryx-future/package/nuget/Moryx.USers) |
| `Moryx.ProcessData` | [![NuGet](https://img.shields.io/nuget/v/Moryx.ProcessData.svg)](https://www.nuget.org/packages/Moryx.ProcessData/) | [![MyGet](https://img.shields.io/myget/moryx/vpre/Moryx.ProcessData)](https://www.myget.org/feed/moryx/package/nuget/Moryx.ProcessData) | [![MyGet-Release](https://img.shields.io/myget/moryx-future/vpre/Moryx.ProcessData)](https://www.myget.org/feed/moryx-future/package/nuget/Moryx.ProcessData) |
| `Moryx.Simulation` | [![NuGet](https://img.shields.io/nuget/v/Moryx.Simulation.svg)](https://www.nuget.org/packages/Moryx.Simulation/) | [![MyGet](https://img.shields.io/myget/moryx/vpre/Moryx.Simulation)](https://www.myget.org/feed/moryx/package/nuget/Moryx.Simulation) | [![MyGet-Release](https://img.shields.io/myget/moryx-future/vpre/Moryx.Simulation)](https://www.myget.org/feed/moryx-future/package/nuget/Moryx.Simulation) |
## Contribute

You can contribute to the MORYX Factory Domain by reporting bugs and suggesting extensions to APIs.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6.1.1
6.2.0
190 changes: 190 additions & 0 deletions docs/articles/Simulation/SimulationDriver.md
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//
```
47 changes: 47 additions & 0 deletions docs/articles/Simulation/SimulationParameters.md
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);
}
}
````
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,21 @@ public class EnumInstructionAttribute : Attribute
/// <summary>
/// The title of the button generated for the enum value marked with this attribute.
/// </summary>
[Obsolete("Use Display Attribute instead")]
public string Title { get; }

/// <summary>
/// Create instruction attribute without title
/// </summary>
public EnumInstructionAttribute()
{

}

/// <summary>
/// Constructor with title
/// </summary>
/// <param name="title">The title</param>
[Obsolete("Use empty constructor and Display Attribute instead")]
public EnumInstructionAttribute(string title)
{
Title = title;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) 2021, Phoenix Contact GmbH & Co. KG
// Licensed under the Apache License, Version 2.0

using Moryx.Tools;
using System;
using System.Collections.Generic;
using System.Linq;
Expand Down Expand Up @@ -41,9 +42,11 @@ public EnumInstructionResult(Type resultEnum, Action<int, object> callback, para
foreach (var name in Enum.GetNames(resultEnum).Except(exceptions))
{
var member = resultEnum.GetMember(name)[0];
// Try to fetch display name or title from attribute
var displayName = member.GetDisplayName();
var attribute = (EnumInstructionAttribute)member.GetCustomAttributes(typeof(EnumInstructionAttribute), false).FirstOrDefault();

var text = attribute?.Title ?? name;
var text = displayName ?? attribute?.Title ?? name;

Check warning on line 49 in src/Moryx.ControlSystem/VisualInstructions/EnumInstructionResult.cs

View workflow job for this annotation

GitHub Actions / Build / Build

'EnumInstructionAttribute.Title' is obsolete: 'Use Display Attribute instead'

Check warning on line 49 in src/Moryx.ControlSystem/VisualInstructions/EnumInstructionResult.cs

View workflow job for this annotation

GitHub Actions / Build / Build

'EnumInstructionAttribute.Title' is obsolete: 'Use Display Attribute instead'

Check warning on line 49 in src/Moryx.ControlSystem/VisualInstructions/EnumInstructionResult.cs

View workflow job for this annotation

GitHub Actions / UnitTests / UnitTests

'EnumInstructionAttribute.Title' is obsolete: 'Use Display Attribute instead'

Check warning on line 49 in src/Moryx.ControlSystem/VisualInstructions/EnumInstructionResult.cs

View workflow job for this annotation

GitHub Actions / UnitTests / UnitTests

'EnumInstructionAttribute.Title' is obsolete: 'Use Display Attribute instead'
allValues[text] = (int)Enum.Parse(resultEnum, name);

if(attribute == null)
Expand Down
31 changes: 31 additions & 0 deletions src/Moryx.Factory/EntryVisualizationAttribute.cs
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; }
}
}
Loading

0 comments on commit c03fc53

Please sign in to comment.