Skip to content

Commit 1e73252

Browse files
chore(v0.2): Improve RPC handling
Add IRpcMethodMarker to flag interfaces to be used for RPC, which makes it easier to extend with new services in future.
1 parent 46b56c3 commit 1e73252

File tree

14 files changed

+152
-44
lines changed

14 files changed

+152
-44
lines changed

.Zeugwerk/config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"name": "TcHaxx.Snappy",
77
"plcs": [
88
{
9-
"version": "0.1.0.0",
9+
"version": "0.2.0.0",
1010
"name": "snappy",
1111
"type": "Library",
1212
"packages": [

.editorconfig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,4 +197,5 @@ csharp_style_prefer_extended_property_pattern = true:suggestion
197197

198198
# SonarAnalyzer
199199
dotnet_diagnostic.S1134.severity = suggestion
200-
dotnet_diagnostic.S1135.severity = suggestion
200+
dotnet_diagnostic.S1135.severity = suggestion
201+
dotnet_diagnostic.S6602.severity = suggestion

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,15 @@ Activate Configuration | Execute | `cmd /c start TcHaxx.Snappy.CLI verify -d \"%
119119
## Source control: Received and Verified files
120120
When dealing with source control, consider the following guidelines for handling **Received** and **Verified** files:
121121

122-
1. **Exclusion**:
122+
1. **Exclude files**:
123123
- Exclude all files with the pattern `*.received.*` from source control.
124124
- To achieve this, add the following line to your `.gitignore` file:
125125
```
126126
*.received.*
127127
```
128128
129-
2. **Commitment**:
130-
- On the other hand, **commit** all files with the pattern `*.verified.*` to source control.
129+
2. **Commit files**:
130+
- **Commit** all files with the pattern `*.verified.*` to source control.
131131
132132
> See [Verify/README](https://github.com/VerifyTests/Verify?tab=readme-ov-file#source-control-received-and-verified-files)
133133
@@ -169,4 +169,5 @@ Option | Required | Default | Description
169169
* [TcUnit](https://github.com/tcunit/TcUnit) - A unit testing framework for Beckhoff's TwinCAT 3
170170
* [CommandLineParser](https://github.com/commandlineparser/commandline) - A command line parsing library for .NET applications.
171171
* [Verify](https://github.com/VerifyTests/Verify) - A library used for snapshot testing.
172-
* [Serilog](https://github.com/serilog/serilog) - A logging library for .NET applications.
172+
* [Serilog](https://github.com/serilog/serilog) - A logging library for .NET applications.
173+
* [TF6000_ADS_DOTNET_V5_Samples](https://github.com/Beckhoff/TF6000_ADS_DOTNET_V5_Samples) - Sample code for the Version 6.X series of the TwinCAT ADS .NET Packages

src/TcHaxx.Snappy.CLI/TcHaxx.Snappy.CLI.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<Description>A Snapshot Testing framework for TwinCAT 3</Description>
1111
<Copyright>Copyright (c) 2024 densogiaichned</Copyright>
1212
<Title>TwinCAT Snapshot Testing framework</Title>
13-
<Version>0.1.0</Version>
13+
<Version>0.2.0</Version>
1414
<AssemblyVersion>$(Version)</AssemblyVersion>
1515
<FileVersion>$(Version)</FileVersion>
1616
<PackAsTool>True</PackAsTool>
Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using TcHaxx.Snappy.Common.Verify;
2-
3-
namespace TcHaxx.Snappy.Common.RPC;
1+
namespace TcHaxx.Snappy.Common.RPC;
42

53
/// <summary>
64
/// Describes an descriptor for RPC methods.
@@ -14,8 +12,8 @@ public interface IRpcMethodDescriptor
1412
public IEnumerable<RpcMethodDescription> GetRpcMethodDescription();
1513

1614
/// <summary>
17-
/// Registers a <see cref="IVerifyMethod"/> implementation with <see cref="IRpcMethodDescriptor"/>.
15+
/// Registers a <see cref="IRpcMethodMarker"/> implementation with <see cref="IRpcMethodDescriptor"/>.
1816
/// </summary>
19-
/// <param name="rpcVerifyMethod"></param>
20-
public void Register(IVerifyMethod rpcVerifyMethod);
17+
/// <param name="rpcMethod"></param>
18+
public void Register(IRpcMethodMarker rpcMethod);
2119
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace TcHaxx.Snappy.Common.RPC;
2+
3+
/// <summary>
4+
/// Empty inteface to mark RPC methods.
5+
/// </summary>
6+
public interface IRpcMethodMarker
7+
{
8+
}
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System.Reflection;
2-
using TcHaxx.Snappy.Common.Verify;
32

43
namespace TcHaxx.Snappy.Common.RPC;
54

6-
public record RpcMethodDescription(MethodInfo Method, IEnumerable<ParameterInfo> Parameters, ParameterInfo ReturnValue, IVerifyMethod RpcInvocableMethod);
5+
public record RpcMethodDescription(MethodInfo Method, IEnumerable<ParameterInfo> Parameters, ParameterInfo ReturnValue, IRpcMethodMarker RpcInvocableMethod, string? Alias)
6+
{
7+
public string InstanceName => RpcInvocableMethod.GetType().FullName!;
8+
}
Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
using TcHaxx.Snappy.Common.Verify;
1+
using System.Data;
2+
using System.Reflection;
3+
using TcHaxx.Snappy.Common.RPC.Attributes;
24

35
namespace TcHaxx.Snappy.Common.RPC;
46

57
public class RpcMethodDescriptor : IRpcMethodDescriptor
68
{
7-
private readonly Queue<IVerifyMethod> _verifyMethods = new();
9+
private readonly Queue<IRpcMethodMarker> _verifyMethods = new();
810

911
public RpcMethodDescriptor()
1012
{
@@ -18,20 +20,57 @@ public IEnumerable<RpcMethodDescription> GetRpcMethodDescription()
1820
}
1921
}
2022

21-
public void Register(IVerifyMethod rpcVerifyMethod)
23+
public void Register(IRpcMethodMarker rpcMethod)
2224
{
23-
_verifyMethods.Enqueue(rpcVerifyMethod);
25+
_verifyMethods.Enqueue(rpcMethod);
2426
}
25-
26-
private RpcMethodDescription Transform(IVerifyMethod rpcVerifyMethod)
27+
private static RpcMethodDescription Transform(IRpcMethodMarker rpcMethod)
2728
{
28-
var method = rpcVerifyMethod.GetType().GetMethod(nameof(IVerifyMethod.Verify)) ??
29-
throw new RpcMethodTransformException($"Method \"{nameof(IVerifyMethod.Verify)}\" not found.");
29+
var method = GetMethodInfo(rpcMethod);
30+
3031
var parameters = method.GetParameters() ??
31-
throw new RpcMethodTransformException($"Expected method \"{nameof(IVerifyMethod.Verify)}\" to have parameters");
32+
throw new RpcMethodTransformException($"Expected method \"{method.Name}\" to have parameters");
3233

3334
var retVal = method.ReturnParameter;
3435

35-
return new RpcMethodDescription(method, parameters, retVal, rpcVerifyMethod);
36+
var alias = GetAliasAttriubte(rpcMethod);
37+
return new RpcMethodDescription(method, parameters, retVal, rpcMethod, alias);
38+
}
39+
40+
private static MethodInfo GetMethodInfo(IRpcMethodMarker rpcMethod)
41+
{
42+
var typeName = rpcMethod.GetType().Name;
43+
44+
var methodInfos = GetMethodInfos(rpcMethod);
45+
46+
if (methodInfos is null || methodInfos.Length == 0)
47+
{
48+
throw new RpcMethodTransformException($"No RPC method found in type \"{typeName}\".");
49+
}
50+
51+
if (methodInfos.Length > 1)
52+
{
53+
throw new RpcMethodTransformException($"Only one RPC method supported per type ({typeName}).");
54+
}
55+
56+
return methodInfos[0];
57+
}
58+
59+
private static string? GetAliasAttriubte(IRpcMethodMarker rpcMethod)
60+
{
61+
var aliasAttribute = rpcMethod.GetType()
62+
.GetMethods()
63+
.Select(x => x.GetCustomAttribute<AliasAttribute>())
64+
.FirstOrDefault(x => x is not null);
65+
return aliasAttribute?.AliasName;
66+
}
67+
68+
private static MethodInfo[]? GetMethodInfos(IRpcMethodMarker rpcMethod)
69+
{
70+
var interfaces = rpcMethod.GetType().GetInterfaces();
71+
var rpcInterfaces = interfaces
72+
.FirstOrDefault(i => typeof(IRpcMethodMarker).IsAssignableFrom(i) && i.GetInterfaces().Length == 1 && i.GetInterfaces().Contains(typeof(IRpcMethodMarker)));
73+
74+
return rpcInterfaces?.GetMethods();
3675
}
3776
}

src/TcHaxx.Snappy.Common/Verify/IVerifyMethod.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
using TcHaxx.Snappy.Common.RPC.Attributes;
1+
using TcHaxx.Snappy.Common.RPC;
2+
using TcHaxx.Snappy.Common.RPC.Attributes;
23

34
namespace TcHaxx.Snappy.Common.Verify;
45

5-
public interface IVerifyMethod
6+
public interface IVerifyMethod : IRpcMethodMarker
67
{
78
public VerificationResult Verify(
89
[String(Constants.DEFAULT_TEST_NAMES_LENGTH)] string testSuiteName,

src/TcHaxx.Snappy.TcADS/SymbolicServer.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,36 @@ protected override void OnConnected()
6060
}
6161

6262
protected override AdsErrorCode OnRpcInvoke(IInterfaceInstance structInstance, IRpcMethod method, object[] values, out object? returnValue)
63+
{
64+
returnValue = null;
65+
var retVal = OnRpcInvokeProxy(structInstance, method, values, out returnValue);
66+
67+
if (retVal != AdsErrorCode.NoError)
68+
{
69+
// Note:
70+
// Due to a bug (?) in ADS.Net, all parameter values and the returnValue must not be NULL!
71+
// Otherwise, it will throw an exception during marshalling, hence the client will only receive error 0x1
72+
returnValue = 0;
73+
PresetParameterValues(ref values);
74+
}
75+
76+
return retVal;
77+
}
78+
79+
private static void PresetParameterValues(ref object[] parameterValues)
80+
{
81+
for (var i = 0; i < parameterValues.Length; i++)
82+
{
83+
if (parameterValues[i] is not null)
84+
{
85+
continue;
86+
}
87+
88+
parameterValues[i] = 0;
89+
}
90+
}
91+
92+
private AdsErrorCode OnRpcInvokeProxy(IInterfaceInstance structInstance, IRpcMethod method, object[] parameterValues, out object? returnValue)
6393
{
6494
var iDataType = structInstance.DataType;
6595
if (iDataType is null)
@@ -68,7 +98,8 @@ protected override AdsErrorCode OnRpcInvoke(IInterfaceInstance structInstance, I
6898
_logger?.LogError("{OnRpcInvoke}: {IDataType} is null", nameof(OnRpcInvoke), nameof(IDataType));
6999
return AdsErrorCode.DeviceInvalidContext;
70100
}
101+
71102
_logger?.LogInformation("{OnRpcInvoke}: Invoking method {IRpcMethod} of {IDataTypeFullName}", nameof(OnRpcInvoke), method, iDataType.FullName);
72-
return _symbolFactory.InvokeRpcMethod(iDataType, values, out returnValue);
103+
return _symbolFactory.InvokeRpcMethod(structInstance, method, parameterValues, out returnValue);
73104
}
74105
}

0 commit comments

Comments
 (0)