Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/dev' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
dbeuchler committed Jan 14, 2021
2 parents 6434ce7 + f03d4d3 commit 9a62ef1
Show file tree
Hide file tree
Showing 29 changed files with 222 additions and 145 deletions.
48 changes: 9 additions & 39 deletions .build/BuildToolkit.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ $NugetPackageArtifacts = "$ArtifactsDir\Packages";
. "$PSScriptRoot\Output.ps1";

# Define Tools
$global:MSBuildCli = "msbuild.exe";
$global:DotNetCli = "dotnet.exe";
$global:NugetCli = "nuget.exe";
$global:GitCli = "";
Expand Down Expand Up @@ -189,7 +188,7 @@ function Invoke-Build([string]$ProjectFile, [string]$Options = "") {
ForEach ($solution in (Get-ChildItem $RootPath -Filter "*.sln")) {
Write-Host "Restoring Nuget packages of $solution";

& $global:NugetCli restore $solution -Verbosity $env:MORYX_NUGET_VERBOSITY -configfile $NugetConfig;
& $global:DotNetCli restore $solution --verbosity $env:MORYX_NUGET_VERBOSITY --configfile $NugetConfig;
Invoke-ExitCodeCheck $LastExitCode;
}

Expand All @@ -198,9 +197,12 @@ function Invoke-Build([string]$ProjectFile, [string]$Options = "") {
$additonalOptions = ",$Options";
}

$params = "Configuration=$env:MORYX_BUILD_CONFIG,Optimize=" + (&{If($env:MORYX_OPTIMIZE_CODE -eq $True) {"true"} Else {"false"}}) + ",DebugSymbols=true$additonalOptions";
$msbuildParams = "Optimize=" + (&{If($env:MORYX_OPTIMIZE_CODE -eq $True) {"true"} Else {"false"}}) + ",DebugSymbols=true$additonalOptions";
$buildArgs = "--configuration", "$env:MORYX_BUILD_CONFIG";
$buildArgs += "--verbosity", $env:MORYX_BUILD_VERBOSITY;
$buildArgs += "-p:$msbuildParams"

& $global:MSBuildCli $ProjectFile /p:$params /verbosity:$env:MORYX_BUILD_VERBOSITY
& $global:DotNetCli build $ProjectFile @buildArgs
Invoke-ExitCodeCheck $LastExitCode;
}

Expand Down Expand Up @@ -353,7 +355,7 @@ function Get-CsprojIsNetCore($CsprojItem) {
if ($null -ne $sdkProject) {
# Read Target Framework
$targetFramework = $csprojContent.Project.PropertyGroup.TargetFramework;
if ($targetFramework -Match "netcoreapp") {
if ($targetFramework -Match "netcoreapp" -or $targetFramework -Match "net5.") {
# NETCore
return $true;
}
Expand Down Expand Up @@ -501,7 +503,7 @@ function Invoke-Publish {

foreach ($package in $packages) {
Write-Host "Pushing package $package"
& $global:NugetCli push $package $env:MORYX_NUGET_APIKEY -Source $env:MORYX_PACKAGE_TARGET -Verbosity $env:MORYX_NUGET_VERBOSITY -NoSymbols
& $global:DotNetCli nuget push $package --api-key $env:MORYX_NUGET_APIKEY --no-symbols true --skip-duplicate --source $env:MORYX_PACKAGE_TARGET
Invoke-ExitCodeCheck $LastExitCode;
}

Expand All @@ -513,7 +515,7 @@ function Invoke-Publish {

foreach ($symbolPackage in $symbolPackages) {
Write-Host "Pushing symbol (snupkg) $symbolPackage"
& $global:NugetCli push $symbolPackage $env:MORYX_NUGET_APIKEY -Source $env:MORYX_PACKAGE_TARGET_V3 -Verbosity $env:MORYX_NUGET_VERBOSITY
& $global:DotNetCli nuget push $symbolPackage --api-key $env:MORYX_NUGET_APIKEY --skip-duplicate --source $env:MORYX_PACKAGE_TARGET_V3
Invoke-ExitCodeCheck $LastExitCode;
}
}
Expand Down Expand Up @@ -645,38 +647,6 @@ function Set-AssemblyVersions([string[]]$Ignored = $(), [string]$SearchPath = $R
}
}

function Set-VsixManifestVersion([string]$VsixManifest) {
$file = Get-Childitem -Path $VsixManifest
if (-Not $file) {
Write-Host "VSIX Manifest: $VsixManifest was not found!"
exit 1;
}

[xml]$manifestContent = Get-Content $file
$manifestContent.PackageManifest.Metadata.Identity.Version = $env:MORYX_ASSEMBLY_VERSION
$manifestContent.Save($VsixManifest)

Write-Host "Version $env:MORYX_ASSEMBLY_VERSION applied to $VsixManifest!"
}

function Set-VsTemplateVersion([string]$VsTemplate) {
$file = Get-Childitem -Path $VsTemplate
if (-Not $file) {
Write-Host "VsTemplate: $VsTemplate was not found!"
exit 1;
}

[xml]$templateContent = Get-Content $VsTemplate

$versionRegex = "(\d+)\.(\d+)\.(\d+)\.(\d+)"

$wizardAssemblyStrongName = $templateContent.VSTemplate.WizardExtension.Assembly -replace $versionRegex, $env:MORYX_ASSEMBLY_VERSION
$templateContent.VSTemplate.WizardExtension.Assembly = $wizardAssemblyStrongName
$templateContent.Save($vsTemplate)

Write-Host "Version$env:MORYX_ASSEMBLY_VERSION applied to $VsTemplate!"
}

function CreateFolderIfNotExists([string]$Folder) {
if (-not (Test-Path $Folder)) {
Write-Host "Creating missing directory '$Folder'"
Expand Down
4 changes: 4 additions & 0 deletions .build/Global.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ClassNeverInstantiated_002ELocal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestUseVarKeywordEverywhere/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestUseVarKeywordEvident/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_EMBEDDED_STATEMENT_ON_SAME_LINE/@EntryValue">NEVER</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SIMPLE_EMBEDDED_STATEMENT_STYLE/@EntryValue">LINE_BREAK</s:String>
<s:Boolean x:Key="/Default/Environment/MemoryUsageIndicator/IsVisible/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
Expand Down
61 changes: 31 additions & 30 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,41 +15,41 @@ env:
MORYX_OPTIMIZE_CODE: "false"
MORYX_BUILD_CONFIG: "Release"
MORYX_BUILDNUMBER: ${{github.run_number}}
dotnet_sdk_version: '5.0.100'
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true

jobs:
Build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2

- name: Setup MSBuild
uses: microsoft/setup-msbuild@v1

- name: Setup NuGet.exe
uses: NuGet/[email protected]

- name: Build
shell: pwsh
run: ./Build.ps1 -Build -Pack

- name: Upload package artifacts
uses: actions/upload-artifact@v2
with:
name: packages
path: artifacts/Packages/
retention-days: 1
- uses: actions/checkout@v2

- name: Setup .NET SDK
uses: actions/setup-dotnet@v1
with:
dotnet-version: ${{ env.dotnet_sdk_version }}

- name: Build
shell: pwsh
run: ./Build.ps1 -Build -Pack

- name: Upload package artifacts
uses: actions/upload-artifact@v2
with:
name: packages
path: artifacts/Packages/
retention-days: 1

UnitTests:
needs: [Build]
runs-on: windows-latest
steps:
- uses: actions/checkout@v2

- name: Setup MSBuild
uses: microsoft/setup-msbuild@v1

- name: Setup NuGet.exe
uses: NuGet/[email protected]
- name: Setup .NET SDK
uses: actions/setup-dotnet@v1
with:
dotnet-version: ${{ env.dotnet_sdk_version }}

- name: Execute Unit Tests
shell: pwsh
Expand All @@ -68,11 +68,10 @@ jobs:
steps:
- uses: actions/checkout@v2

- name: Setup MSBuild
uses: microsoft/setup-msbuild@v1

- name: Setup NuGet.exe
uses: NuGet/[email protected]
- name: Setup .NET SDK
uses: actions/setup-dotnet@v1
with:
dotnet-version: ${{ env.dotnet_sdk_version }}

- name: Execute Integration Tests
shell: pwsh
Expand Down Expand Up @@ -124,8 +123,10 @@ jobs:
steps:
- uses: actions/checkout@v2

- name: Setup NuGet.exe
uses: NuGet/[email protected]
- name: Setup .NET SDK
uses: actions/setup-dotnet@v1
with:
dotnet-version: ${{ env.dotnet_sdk_version }}

- name: Download package artifacts
uses: actions/download-artifact@v2
Expand Down
24 changes: 19 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,14 @@
<a href="https://github.com/PHOENIXCONTACT/MORYX-AbstractionLayer/workflows">
<img src="https://github.com/PHOENIXCONTACT/MORYX-AbstractionLayer/workflows/CI/badge.svg" alt="CI">
</a>
<a href="https://www.myget.org/feed/Packages/moryx">
<img src="https://img.shields.io/myget/moryx/v/Moryx.AbstractionLayer" alt="MyGet">
</a>
<a href="https://codecov.io/gh/PHOENIXCONTACT/MORYX-AbstractionLayer/branch/dev">
<img alt="Coverage" src="https://codecov.io/gh/PHOENIXCONTACT/MORYX-AbstractionLayer/coverage.svg?branch=dev" />
</a>
<a href="https://github.com/PHOENIXCONTACT/MORYX-AbstractionLayer/blob/dev/LICENSE">
<img src="https://img.shields.io/github/license/PHOENIXCONTACT/MORYX-AbstractionLayer" alt="License">
</a>
<a href="https://gitter.im/MORYX-Industry/Framework?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge">
<img src="https://badges.gitter.im/MORYX-Industry/Framework.svg" alt="Gitter">
<a href="https://gitter.im/PHOENIXCONTACT/MORYX?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge">
<img src="https://badges.gitter.im/PHOENIXCONTACT/MORYX.svg" alt="Gitter">
</a>
</p>

Expand All @@ -33,6 +30,23 @@ The **MORYX AbstractionLayer** is the environment for the digital twins of resou

## Getting Started

If you want to start developing with or for MORYX, the easiest way is our [template repository](https://github.com/PHOENIXCONTACT/MORYX-Template). It comes with two empty solutions, the necessary package feeds and preinstalled empty MORYX runtime. Add projects and packages to backend and frontend solutions depending on your specific requirements. Install stable releases via Nuget; development releases are available via MyGet.

| Package Name | Release (NuGet) | CI (MyGet) |
|--------------|-----------------|------------|
| `Moryx.AbstractionLayer` | [![NuGet](https://img.shields.io/nuget/v/Moryx.AbstractionLayer.svg)](https://www.nuget.org/packages/Moryx.AbstractionLayer/) | [![MyGet](https://img.shields.io/myget/moryx/vpre/Moryx.AbstractionLayer)](https://www.myget.org/feed/moryx/package/nuget/Moryx.AbstractionLayer) |
| `Moryx.AbstractionLayer.TestTools` | [![NuGet](https://img.shields.io/nuget/v/Moryx.AbstractionLayer.TestTools.svg)](https://www.nuget.org/packages/Moryx.AbstractionLayer.TestTools/) | [![MyGet](https://img.shields.io/myget/moryx/vpre/Moryx.AbstractionLayer.TestTools)](https://www.myget.org/feed/moryx/package/nuget/Moryx.AbstractionLayer.TestTools) |
| `Moryx.Notifications` | [![NuGet](https://img.shields.io/nuget/v/Moryx.Notifications.svg)](https://www.nuget.org/packages/Moryx.Notifications/) | [![MyGet](https://img.shields.io/myget/moryx/vpre/Moryx.Notifications)](https://www.myget.org/feed/moryx/package/nuget/Moryx.Notifications) |
| `Moryx.Products.Management` | [![NuGet](https://img.shields.io/nuget/v/Moryx.Products.Management.svg)](https://www.nuget.org/packages/Moryx.Products.Management/) | [![MyGet](https://img.shields.io/myget/moryx/vpre/Moryx.Products.Management)](https://www.myget.org/feed/moryx/package/nuget/Moryx.Products.Management) |
| `Moryx.Products.Model` | [![NuGet](https://img.shields.io/nuget/v/Moryx.Products.Model.svg)](https://www.nuget.org/packages/Moryx.Products.Model/) | [![MyGet](https://img.shields.io/myget/moryx/vpre/Moryx.Products.Model)](https://www.myget.org/feed/moryx/package/nuget/Moryx.Products.Model) |
| `Moryx.Resources.Interaction` | [![NuGet](https://img.shields.io/nuget/v/Moryx.Resources.Interaction.svg)](https://www.nuget.org/packages/Moryx.Resources.Interaction/) | [![MyGet](https://img.shields.io/myget/moryx/vpre/Moryx.Resources.Interaction)](https://www.myget.org/feed/moryx/package/nuget/Moryx.Resources.Interaction) |
| `Moryx.Resources.Management` | [![NuGet](https://img.shields.io/nuget/v/Moryx.Resources.Management.svg)](https://www.nuget.org/packages/Moryx.Resources.Management/) | [![MyGet](https://img.shields.io/myget/moryx/vpre/Moryx.Resources.Management)](https://www.myget.org/feed/moryx/package/nuget/Moryx.Resources.Management) |
| `Moryx.AbstractionLayer.UI` | [![NuGet](https://img.shields.io/nuget/v/Moryx.AbstractionLayer.UI.svg)](https://www.nuget.org/packages/Moryx.AbstractionLayer.UI/) | [![MyGet](https://img.shields.io/myget/moryx/vpre/Moryx.AbstractionLayer.UI)](https://www.myget.org/feed/moryx/package/nuget/Moryx.AbstractionLayer.UI) |
| `Moryx.Products.UI` | [![NuGet](https://img.shields.io/nuget/v/Moryx.Products.UI.svg)](https://www.nuget.org/packages/Moryx.Products.UI/) | [![MyGet](https://img.shields.io/myget/moryx/vpre/Moryx.Products.UI)](https://www.myget.org/feed/moryx/package/nuget/Moryx.Products.UI) |
| `Moryx.Products.UI.Interaction` | [![NuGet](https://img.shields.io/nuget/v/Moryx.Products.UI.Interaction.svg)](https://www.nuget.org/packages/Moryx.Products.UI.Interaction/) | [![MyGet](https://img.shields.io/myget/moryx/vpre/Moryx.Products.UI.Interaction)](https://www.myget.org/feed/moryx/package/nuget/Moryx.Products.UI.Interaction) |
| `Moryx.Resources.UI` | [![NuGet](https://img.shields.io/nuget/v/Moryx.Resources.UI.svg)](https://www.nuget.org/packages/Moryx.Resources.UI/) | [![MyGet](https://img.shields.io/myget/moryx/vpre/Moryx.Resources.UI)](https://www.myget.org/feed/moryx/package/nuget/Moryx.Resources.UI) |
| `Moryx.Resources.UI.Interaction` | [![NuGet](https://img.shields.io/nuget/v/Moryx.Resources.UI.Interaction.svg)](https://www.nuget.org/packages/Moryx.Resources.UI.Interaction/) | [![MyGet](https://img.shields.io/myget/moryx/vpre/Moryx.Resources.UI.Interaction)](https://www.myget.org/feed/moryx/package/nuget/Moryx.Resources.UI.Interaction) |

Whether you want to debug and experiment with this repository or build an application based on the Abstraction Layers packages you need to follow a few simple steps to setup each of the modules. For both modules this requires the package *Moryx.Runtime.Maintenance.Web* and its [database configuration](http://localhost/maintenanceweb/#/databases).

**Product Management**:
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
5.0.0
5.1.0
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net45</TargetFrameworks>
<TargetFrameworks>netstandard2.0;net45;net5.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Description>Additional AbstractionLayer implementations for Tests</Description>
<CreatePackage>true</CreatePackage>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<TargetFramework>net45</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<UseWPF>true</UseWPF>
<Description>Moryx.AbstractionLayer.UI</Description>
<Description>Common package for AbstractionLayer UI packages</Description>
<CreatePackage>true</CreatePackage>
</PropertyGroup>

Expand Down
43 changes: 43 additions & 0 deletions src/Moryx.AbstractionLayer/Activities/Parameters.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) 2020, Phoenix Contact GmbH & Co. KG
// Licensed under the Apache License, Version 2.0

using System;
using Moryx.Tools;

namespace Moryx.AbstractionLayer
{
/// <summary>
/// Base class for parameters
/// </summary>
#pragma warning disable 618 //TODO: Remove if ParametersBase was removed
public abstract class Parameters : ParametersBase
{
/// <summary>
/// Method to create a new instance of this parameters
/// </summary>
private Func<Parameters> _instanceDelegate;

/// <summary>
/// Resolve the binding parameters
/// </summary>
/// <param name="process">Process to bind to</param>
/// <returns>Parameters with resolved bindings</returns>
protected sealed override ParametersBase ResolveBinding(IProcess process)
{
if (_instanceDelegate == null)
_instanceDelegate = ReflectionTool.ConstructorDelegate<Parameters>(GetType());

var instance = _instanceDelegate();
Populate(process, instance);
return instance;
}

/// <summary>
/// Populates the given instance with parameters
/// </summary>
/// <param name="instance">New instance of this type</param>
/// <param name="process">Process to bind to</param>
protected abstract void Populate(IProcess process, Parameters instance);
}
#pragma warning restore 618
}
18 changes: 11 additions & 7 deletions src/Moryx.AbstractionLayer/Activities/ParametersBase.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
// Copyright (c) 2020, Phoenix Contact GmbH & Co. KG
// Licensed under the Apache License, Version 2.0

using System;

namespace Moryx.AbstractionLayer
{
/// <summary>
/// This is a protected base class that can not not be extended outside this assembly
/// Outdated base class for parameters use <see cref="Parameters"/> instead
/// </summary>
[Obsolete("Use '" + nameof(Parameters) + "' instead!")]
public abstract class ParametersBase : IParameters
{
private static ProcessBindingResolverFactory _resolverFactory;
private IProcess _process;

/// <summary>
/// Singleton resolver factory for process parameter binding
/// </summary>
protected static ProcessBindingResolverFactory ResolverFactory => _resolverFactory ?? (_resolverFactory = new ProcessBindingResolverFactory());

/// <see cref="IParameters"/>
public IParameters Bind(IProcess process)
{
Expand All @@ -21,12 +31,6 @@ public IParameters Bind(IProcess process)
return resolved;
}

private static ProcessBindingResolverFactory _resolverFactory;
/// <summary>
/// Singleton resolver factory for process parameter binding
/// </summary>
protected static ProcessBindingResolverFactory ResolverFactory => _resolverFactory ?? (_resolverFactory = new ProcessBindingResolverFactory());

/// <summary>
/// Resolve the binding parameters
/// </summary>
Expand Down
4 changes: 2 additions & 2 deletions src/Moryx.AbstractionLayer/Moryx.AbstractionLayer.csproj
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net45</TargetFrameworks>
<TargetFrameworks>netstandard2.0;net45;net5.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Description>AbstractionLayer</Description>
<Description>Domain model types and definitions of Cyber-physical systems in IIoT projects.</Description>
<CreatePackage>true</CreatePackage>
</PropertyGroup>

Expand Down
2 changes: 1 addition & 1 deletion src/Moryx.AbstractionLayer/Tasks/TaskStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace Moryx.AbstractionLayer
[DataContract(IsReference = true)]
public abstract class TaskStep<TActivity, TProcParam, TParam> : WorkplanStepBase, ITaskStep<TParam>
where TActivity : IActivity<TProcParam>, new()
where TProcParam : IParameters, new()
where TProcParam : IParameters
where TParam : TProcParam, new()
{
private IIndexResolver _indexResolver;
Expand Down
4 changes: 2 additions & 2 deletions src/Moryx.Notifications/Moryx.Notifications.csproj
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net45</TargetFrameworks>
<TargetFrameworks>netstandard2.0;net45;net5.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Description>Notifications</Description>
<Description>Types and interfaces for notifications within MORYX applications</Description>
<CreatePackage>true</CreatePackage>
</PropertyGroup>

Expand Down
Loading

0 comments on commit 9a62ef1

Please sign in to comment.