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
32 changes: 32 additions & 0 deletions .github/workflows
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# This is a basic workflow to help you get started with Actions
name: CI

# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the master branch
push:
branches: [ master ]
pull_request:
branches: [ master ]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: windows-latest

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- name: check out repository code
uses: actions/checkout@v2

# Build .Net Source
- name: Execut cake to build .NET
working-directory: NET
run: .\build.ps1 -t Test
shell: powershell
17 changes: 17 additions & 0 deletions NET/AdsMapperCli/AdsMapperCli.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net48</TargetFramework>
<StartupObject>AdsMapperCli.Program</StartupObject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Text.Json" Version="5.0.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Mbc.Pcs.Net\Mbc.Pcs.Net.csproj" />
</ItemGroup>

</Project>
96 changes: 96 additions & 0 deletions NET/AdsMapperCli/DestinationDataObject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using Mbc.Pcs.Net.State;
using System;

namespace AdsMapperCli
{
/* PLC Struct:
TYPE ST_Test :
STRUCT
bBoolValue1 : BOOL := TRUE;
nByteValue1 : BYTE := 255;
nSbyteValue1 : BYTE := 127;
nUshortValue1 : UINT := 65535;
nShortValue1 : INT := 32767;
nUintValue1 : UDINT := 4294967295;
nIntValue1 : DINT := 2147483647;
fFloatValue1 : REAL := -1.11;
fDoubleValue1 : LREAL := 1.11;
fDoubleValue2 : LREAL := 2.22;
fDoubleValue3 : LREAL := 3.33;
fDoubleValue4MappedName : LREAL := 4.44;
tPlcTimeValue1 : TIME := T#1H33M44S555MS;
dPlcDateValue1 : DATE := D#2021-08-30;
dtPlcDateTimeValue1 : DATE_AND_TIME := DT#2021-08-30-11:12:13;
aIntArrayValue : ARRAY[0..2] OF DINT := [1, 2, 3];
eEnumStateValue : E_State := E_State.eRunning;
sPlcVersion : STRING(10) := '21.08.30.0';
sUtf7String : STRING(6) := 'ÄÖö@Ü7';
wsUnicodeString : WSTRING(6) := "ÄÖö@Ü8";
END_STRUCT
END_TYPE

{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_State :
(
eNone := 0,
eStartup := 1,
eRunning := 2,
eStop := 3
);
END_TYPE
*/

public class DestinationDataObject : IPlcState
{
#region " PlcAdsStateReader requires IPlcState interface "

/// <summary>
/// SPS Zeitstempel der Status Daten
/// </summary>
public DateTime PlcTimeStamp { get; set; }

/// <summary>
/// Güte der Status Daten
/// </summary>
public PlcDataQuality PlcDataQuality { get; set; }

#endregion

public bool BoolValue1 { get; set; }
public byte ByteValue1 { get; set; }
public sbyte SbyteValue1 { get; set; }
public ushort UshortValue1 { get; set; }
public short ShortValue1 { get; set; }
public uint UintValue1 { get; set; }
public int IntValue1 { get; set; }
public float FloatValue1 { get; set; }
public double DoubleValue1 { get; set; }
public double DoubleValue2 { get; set; }
public double DoubleValue3 { get; set; }
public double DoubleValue4MappedName { get; set; }
public TimeSpan PlcTimeValue1 { get; set; }
public DateTime PlcDateValue1 { get; set; }
public DateTime PlcDateTimeValue1 { get; set; }
public int[] IntArrayValue { get; set; } = new int[3];
public State EnumStateValue { get; set; }
public string PlcVersion { get; set; }
public string Utf7String { get; set; }
public string UnicodeString { get; set; }

//public Motor MotorObject { get; set; }
}

public class Motor
{
public double ActualSpeed { get; set; }
}

public enum State
{
None = 0,
Startup = 1,
Running = 2,
Stop = 3,
}
}
99 changes: 99 additions & 0 deletions NET/AdsMapperCli/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using Mbc.Ads.Mapper;
using Mbc.Pcs.Net.Connection;
using Mbc.Pcs.Net.State;
using NLog;
using System;

namespace AdsMapperCli
{
public static class Program
{
private static readonly ILogger _logger = LogManager.GetCurrentClassLogger();
private static PlcAdsConnectionService _adsConnectionService;
private static PlcAdsStateReader<DestinationDataObject> _plcAdsTestPlaceStatus;

public static void Main(string[] args)
{
SetupNLog();
try
{
string amsnetid = "172.28.85.92.1.1";
_logger.Info("Setup & Connect to TwinCat on {0}", amsnetid);
_adsConnectionService = new PlcAdsConnectionService(amsnetid, 851);
_adsConnectionService.ConnectionStateChanged += OnConnectionStateChanged;

var testPlaceStatusConfig = new PlcAdsStateReaderConfig<DestinationDataObject>
{
VariablePath = $"PCS_Status.stTest",
AdsMapperConfiguration = new AdsMapperConfiguration<DestinationDataObject>(
cfg => cfg.ForAllSourceMember(opt => opt.RemovePrefix("f", "n", "b", "a", "e", "t", "d", "dt", "s", "ws"))),
CycleTime = TimeSpan.FromMilliseconds(2),
MaxDelay = TimeSpan.FromMilliseconds(500),
};

// Setup state Reader
_plcAdsTestPlaceStatus = new PlcAdsStateReader<DestinationDataObject>(_adsConnectionService, testPlaceStatusConfig);
_plcAdsTestPlaceStatus.StatesChanged += OnPlcStatesChange;

// RocknRoll
_adsConnectionService.Start();

// Wait for termination
var keepRunning = true;
Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e)
{
e.Cancel = true;
keepRunning = false;
};

while (keepRunning) { }

_logger.Info("stopping output");
_plcAdsTestPlaceStatus.StopSampling();
}
catch (Exception ex)
{
_logger.Info(ex, "houston we have a problem: {0}", ex.Message);
}
finally
{
_plcAdsTestPlaceStatus?.Dispose();
_adsConnectionService?.Dispose();
}
}

private static void SetupNLog()
{
var config = new NLog.Config.LoggingConfiguration();
// Targets where to log to: Console
var logconsole = new NLog.Targets.ConsoleTarget("logconsole");
// Rules for mapping loggers to targets
config.AddRule(LogLevel.Info, LogLevel.Fatal, logconsole);
// Apply config
NLog.LogManager.Configuration = config;
}

private static void OnConnectionStateChanged(object sender, PlcConnectionChangeArgs e)
{
if (e.Connected)
{
_logger.Info("Connected to TwinCat");
_plcAdsTestPlaceStatus.StartSampling();
}
else
{
Console.WriteLine("Disconnected to TwinCat");
_plcAdsTestPlaceStatus.StopSampling();
}
}

private static void OnPlcStatesChange(object source, PlcMultiStateChangedEventArgs<DestinationDataObject> testPlaceStatusEvent)
{
var config = new System.Text.Json.JsonSerializerOptions() { WriteIndented = true, Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping };
config.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter());

_logger.Info("New State");
_logger.Info(System.Text.Json.JsonSerializer.Serialize(testPlaceStatusEvent.State, config));
}
}
}
18 changes: 14 additions & 4 deletions NET/Mbc.Ads.Mapper.Test/AdsMapperTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public void AdsMappingConfigurationShouldMapAdsStreamToDataObject()
{
// Arrange
AdsMapperConfiguration<DestinationDataObject> config = new AdsMapperConfiguration<DestinationDataObject>(
cfg => cfg.ForAllSourceMember(opt => opt.RemovePrefix('f', 'n', 'b', 'a', 'e'))
cfg => cfg.ForAllSourceMember(opt => opt.RemovePrefix("f", "n", "b", "a", "e", "t", "d", "dt", "s", "ws"))
.ForMember(dest => dest.DoubleValue4MappedName, opt => opt.MapFrom("fdoublevalue4"))
.ForMember(dest => dest.DoubleValue4MappedName, opt => opt.ConvertFromSourceUsing(value => ((double)value) / 2)));

Expand Down Expand Up @@ -58,14 +58,18 @@ public void AdsMappingConfigurationShouldMapAdsStreamToDataObject()
mappedResult.IntArrayValue[2].Should().Be(102);

mappedResult.EnumStateValue.Should().Be(State.Running);

mappedResult.PlcVersion.Should().Be("21.08.30.0");
mappedResult.Utf7String.Should().Be("ÄÖö@Ü7");
mappedResult.UnicodeString.Should().Be("ÄÖö@Ü8");
}

[Fact]
public void AdsMappingConfigurationPerformance()
{
// Arrange
AdsMapperConfiguration<DestinationDataObject> config = new AdsMapperConfiguration<DestinationDataObject>(
cfg => cfg.ForAllSourceMember(opt => opt.RemovePrefix('f', 'n', 'b', 'a', 'e'))
cfg => cfg.ForAllSourceMember(opt => opt.RemovePrefix("f", "n", "b", "a", "e", "t", "d", "dt", "s", "ws"))
.ForMember(dest => dest.DoubleValue4MappedName, opt => opt.MapFrom("fdoublevalue4"))
.ForMember(dest => dest.DoubleValue4MappedName, opt => opt.ConvertFromSourceUsing(value => ((double)value) / 2)));
AdsMapper<DestinationDataObject> mapper = config.CreateAdsMapper(_fakePlcData.AdsSymbolInfo);
Expand Down Expand Up @@ -161,7 +165,7 @@ public void AdsMappingConfigurationShouldMapDataObjectToAdsStream()
{
// Arrange
AdsMapperConfiguration<DestinationDataObject> config = new AdsMapperConfiguration<DestinationDataObject>(
cfg => cfg.ForAllSourceMember(opt => opt.RemovePrefix("f", "n", "b", "a", "e", "t", "d", "dt"))
cfg => cfg.ForAllSourceMember(opt => opt.RemovePrefix("f", "n", "b", "a", "e", "t", "d", "dt", "s", "ws"))
.ForMember(dest => dest.DoubleValue4MappedName, opt => opt.MapFrom("fdoublevalue4"))
.ForMember(dest => dest.DoubleValue4MappedName, opt => opt.ConvertToSourceUsing((value, type) => value * 2)));
var dataObject = new DestinationDataObject
Expand All @@ -183,9 +187,12 @@ public void AdsMappingConfigurationShouldMapDataObjectToAdsStream()
PlcDateTimeValue1 = new DateTime(2018, 08, 30, 19, 33, 44),
IntArrayValue = new int[] { 100, 101, 102 },
EnumStateValue = State.Running,
PlcVersion = "21.08.30.0",
Utf7String = "ÄÖö@Ü7",
UnicodeString = "ÄÖö@Ü8",
};
byte[] expectedData = _fakePlcData.AdsStream.ToArray();
Array.Clear(expectedData, 82, 8); // nested MotorObject
Array.Clear(expectedData, 114, 8); // nested MotorObject

// Act
AdsMapper<DestinationDataObject> mapper = config.CreateAdsMapper(_fakePlcData.AdsSymbolInfo);
Expand Down Expand Up @@ -217,6 +224,9 @@ private class DestinationDataObject
public DateTime PlcDateTimeValue1 { get; set; }
public int[] IntArrayValue { get; set; } = new int[3];
public State EnumStateValue { get; set; }
public string PlcVersion { get; set; }
public string Utf7String { get; set; }
public string UnicodeString { get; set; }
public Motor MotorObject { get; set; }
}

Expand Down
Loading