AdsSymbolicServer Class
AdsServer class that supports Symbolic information and Notifications. Implements the AdsServer
Inheritance Hierarchy
System.Object
TwinCAT.Ads.Server.AdsServer
TwinCAT.Ads.Server.AdsSymbolicServer
Namespace: TwinCAT.Ads.Server
Assembly: TwinCAT.Ads.SymbolicServer (in
TwinCAT.Ads.SymbolicServer.dll) Version: 6.0.328+39e3229
Syntax
C#
public abstract class AdsSymbolicServer : AdsServer,
ISymbolProvider, ISymbolServer
The AdsSymbolicServer type exposes the following members.
Properties
|
Name |
Description |
---|---|---|
|
Gets the the internal AmsServer object. (Inherited from AdsServer.) | |
|
Gets the data types | |
|
Gets/Sets the default value encoding. | |
|
Gets or sets the instance path encoding (Default is the Windows CodePage) | |
|
Gets a value indicating whether AdsServer is connected. (Inherited from AdsServer.) | |
|
Indicates, that the AdsServer is actually disconnecting. (Inherited from AdsServer.) | |
|
Gets a value indicating whether this instance is disposed. (Inherited from AdsServer.) | |
|
Gets a value indicating this AdsServer has a dynamic/unfixed port. (Inherited from AdsServer.) | |
|
Gets the logger object. (Inherited from AdsServer.) | |
|
Gets the Platform pointer size of this Server | |
|
Gets the name of the root namespace | |
|
The AMS address of this server. (Inherited from AdsServer.) | |
|
Gets the name of the server. (Inherited from AdsServer.) | |
|
Gets the server port. (Inherited from AdsServer.) | |
|
Gets the Version of the Server. (Inherited from AdsServer.) | |
|
Gets the symbols. |
Methods
|
Name |
Description |
---|---|---|
|
Sends an ADS Add Device Notification request (synchronous). (Inherited from AdsServer.) | |
|
Sends an ADS Add Device Notification request (async) (Inherited from AdsServer.) | |
|
Sends an ADS Add Device Notification response. (Inherited from AdsServer.) | |
|
Connect this ADS server to the local ADS router. (Inherited from AdsServer.) | |
|
Registers the AdsServer at the router asynchronously. (Inherited from AdsServer.) | |
|
Sends an ADS Delete Device Notification request (synchronous). (Inherited from AdsServer.) | |
|
Sends an ADS Delete Device Notification request (async). (Inherited from AdsServer.) | |
|
Sends an ADS Delete Device Notification response. (Inherited from AdsServer.) | |
|
Sends an ADS Device Notification request asynchronously (Inherited from AdsServer.) | |
|
Sends an ADS Device Notification request (sync) (Inherited from AdsServer.) | |
|
Disconnects this ADS server from the local ADS router. (Inherited from AdsServer.) | |
|
Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. (Inherited from AdsServer.) | |
|
Releases unmanaged and - optionally - managed resources. (Inherited from AdsServer.) | |
|
Determines whether the specified object is equal to the current object. (Inherited from Object.) | |
|
Finalizes an instance of the AdsServer class. (Inherited from AdsServer.) | |
|
Fires a single notification to the specified Address. | |
|
Fires outstanding cyclic notifications. | |
|
Check internal tables for outstanding cyclic Notifications and fire them asynchronously. | |
|
Gets the data types asynchronously. | |
|
Serves as the default hash function. (Inherited from Object.) | |
|
Gets the name of the server. (Inherited from AdsServer.) | |
|
Gets the symbols asynchronously | |
|
Gets the Type of the current instance. (Inherited from Object.) | |
|
Determines whether the value of the Symbol has changed. | |
|
Creates a shallow copy of the current Object. (Inherited from Object.) | |
|
Called when an ADS Add Device Notification indication is received. (Overrides AdsServer.OnAddDeviceNotificationAsync(AmsAddress, UInt32, UInt32, UInt32, Int32, AmsAddress, NotificationSettings, CancellationToken).) | |
|
Called when an ADS Add Device Notification confirmation is received. (Inherited from AdsServer.) | |
|
Handler function that is called, when the AdsServer is connected, but before calling OnConnected.. (Overrides AdsServer.OnBeforeConnected..) | |
|
Handler function that is called, when the AdsServer is connected. (Overrides AdsServer.OnConnected..) | |
|
Handler function to create the Initial Symbols of the server. | |
|
Called when a DeleteDeviceNotification request is indicated. (Overrides AdsServer.OnDeleteDeviceNotificationAsync(AmsAddress, UInt32, UInt32, CancellationToken).) | |
|
Called when an ADS Delete Device Notification confirmation is received. (Inherited from AdsServer.) | |
|
Called when the AdsServer is about to be disconnected. (Overrides AdsServer.OnDisconnect..) | |
|
Handler function for fireing notifications to the specified targets. | |
|
Handler function for firering notifications to the specified targets. | |
|
Handler function to determine the (stored) value of the symbol from the internal caches of the AdsSymbolicServer. | |
|
Called when an ADS Read indication is received. (Overrides AdsServer.OnReadAsync(AmsAddress, UInt32, UInt32, UInt32, Int32, CancellationToken).) | |
|
Called when an ADS Read confirmation is received. (Inherited from AdsServer.) | |
|
Called when an ADS Read Device Info confirmation is received. (Inherited from AdsServer.) | |
|
Called when an ADS Read State indication is received. (Inherited from AdsServer.) | |
|
Called when an ADS Read State confirmation is received. (Inherited from AdsServer.) | |
|
Handler function for handling pure IG/IO Read indications | |
|
Handler function for Reading the internal value data in raw format. | |
|
Called when an ADS Read Write indication is received. (Overrides AdsServer.OnReadWriteAsync(AmsAddress, UInt32, UInt32, UInt32, Int32, ReadOnlyMemory.Byte., CancellationToken).) | |
|
Called when an ADS Read Write confirmation is received. (Inherited from AdsServer.) | |
|
Handler function for handling of pure IG/IO calls. | |
|
Handler Function for a Router Notification. (Inherited from AdsServer.) | |
|
Handler function called when an RpcInvoke occurs. | |
|
Handles the ServerConnectionStateChanged event. (Inherited from AdsServer.) | |
|
Handler function to store a new Symbol value within internal caches of the AdsSymbolicServer. | |
|
Called when an ADS Write indication is received. (Overrides AdsServer.OnWriteAsync(AmsAddress, UInt32, UInt32, UInt32, ReadOnlyMemory.Byte., CancellationToken).) | |
|
Called when an ADS Write confirmation is received. (Inherited from AdsServer.) | |
|
Called when an ADS Write Control indication is received. (Inherited from AdsServer.) | |
|
Called when an ADS Write Control confirmation is received. (Inherited from AdsServer.) | |
|
Handler function for handling of pure Write IG/IO Indications. | |
|
Handler function for writing the symbol value data in raw format. | |
|
Sends an ADS Read Device Info request asynchronously (Inherited from AdsServer.) | |
|
Sends an ADS Read Device Info request synchronously. (Inherited from AdsServer.) | |
|
Sends an ADS Read Device Info response. (Inherited from AdsServer.) | |
|
Sends an ADS Read State request (asynchronous) (Inherited from AdsServer.) | |
|
Sends an ADS Read State request (synchronous) (Inherited from AdsServer.) | |
|
Sends an ADS Read State response. (Inherited from AdsServer.) | |
|
Sends an ADS Read Request. (Inherited from AdsServer.) | |
|
Sends an ADS Read Request asynchronously. (Inherited from AdsServer.) | |
|
Sends an ADS Read response. (Inherited from AdsServer.) | |
|
Sends an ADS Read Write request. (Inherited from AdsServer.) | |
|
Sends an ADS Read Write request synchronously (Inherited from AdsServer.) | |
|
Sends an ADS Read Write Response. (Inherited from AdsServer.) | |
|
Resets the cached symbolic data. | |
|
Sets the value of the symbol. | |
|
Sets the value of the symbol. | |
|
Returns a string that represents the current object. (Inherited from Object.) | |
|
Tries to get the symbols from the device target. | |
|
Tries to geth the symbols from the device target. | |
|
Sends an ADS Write Control request (synchronous) (Inherited from AdsServer.) | |
|
Sends an ADS Write Control request (asynchronous). (Inherited from AdsServer.) | |
|
Sends an ADS Write Control request (synchronous). (Inherited from AdsServer.) | |
|
Sends an ADS Write Control response. (Inherited from AdsServer.) | |
|
Sends an ADS Write request synchronously. (Inherited from AdsServer.) | |
|
Sends an ADS Write request asynchronously. (Inherited from AdsServer.) | |
|
Sends an ADS Write response. (Inherited from AdsServer.) |
Events
|
Name |
Description |
---|---|---|
|
The connection status has changed (Inherited from AdsServer.) |
Fields
|
Name |
Description |
---|---|---|
|
The handle table (InstancePath --> Symbol handle) | |
|
The notification table | |
|
The notification trigger Source | |
|
The version of the AdsServer (Inherited from AdsServer.) | |
|
The symbol factory | |
|
The SymbolName marshaler | |
|
The symbol version as respond by SymbolVersion |
Remarks
DataTypes and hierarchical trees of symbols can be downloaded from this by symbolic information access. The symbolic access is necessary for the following functionality:
- Browsing of Symbols and DataTypes
- Type Safe Read/Write access of Process Values of AdsServers
This symbolic access is for example used by the following TwinCAT tools:
- TwinCAT Scope
- TwinCAT Powershell Management module TcXaeMgmt
Examples
The following sample shows how to derive from the AdsServer class and create your own Customized ADS Server.
C#
public class SymbolicTestServer : AdsSymbolicServer
{
/// <summary>
/// AmsPort of the SymbolicTestServer
/// </summary>
/// <remarks>
/// User Server Ports must be in between
/// AmsPort.CUSTOMER_FIRST (25000) <= PORT <= AmsPort.CUSTOMER_LAST (25999)
/// to not conflict with Beckhoff prereserved servers!
/// </remarks>
static ushort s_Port = 25000;
/// <summary>
/// Dictionary containing the Values of the Symbols (Symbol -- Value)
/// </summary>
Dictionary<ISymbol, object> _symbolValues = new Dictionary<ISymbol, object>();
/// <summary>
/// Gets the SymbolValue Dictionary of the <see cref="SymbolicTestServer"/>.
/// </summary>
/// <value>The symbol values.</value>
public IDictionary<ISymbol, object> SymbolValues => _symbolValues;
/// <summary>
/// Symbolic Marshaler for Values
/// </summary>
SymbolicAnyTypeMarshaler _symbolMarshaler = new SymbolicAnyTypeMarshaler();
/// <summary>
/// Initializes a new instance of the <see cref="SymbolicTestServer"/> class.
/// </summary>
public SymbolicTestServer()
: base(s_Port, "SymbolicTestServer", null)
{
/// AMS Router enpoint can be changed via envrionment variables which is
/// benefitial in containerized setups where the AMS router is not listening
/// at the default loopback address and port
IPAddress ipEndpoint;
if (!IPAddress.TryParse(System.Environment.GetEnvironmentVariable("ENV_AmsConfiguration__LoopbackAddress"), out ipEndpoint))
{
ipEndpoint = IPAddress.Loopback;
}
int port;
if (!int.TryParse(System.Environment.GetEnvironmentVariable("ENV_AmsConfiguration__LoopbackPort"), out port))
{
port = 48898;
}
AmsConfiguration.RouterEndPoint = new IPEndPoint(ipEndpoint, port);
}
IDisposable _changeValueObserver = null;
/// <summary>
/// Called when [connected].
/// </summary>
protected override void OnConnected()
{
this.AddSymbols()
.AddNotificationTrigger();
// An Observable.Interval is used to simulate changed values (on a 1 Second base)
IObservable<long> changeValueTrigger = Observable.Interval(TimeSpan.FromSeconds(1.0), Scheduler.Default);
_changeValueObserver = changeValueTrigger.Subscribe(toggleValues);
base.OnConnected();
}
protected override bool OnDisconnect()
{
if (_changeValueObserver != null)
_changeValueObserver.Dispose();
return base.OnDisconnect();
}
private void toggleValues(long count)
{
SetValue("Globals.int1", (short)count);
}
/// <summary>
/// Creates an Notification trigger for the Notifications base tick.
/// </summary>
private SymbolicTestServer AddNotificationTrigger()
{
base.notificationTrigger.Add(new BaseTickTrigger(TimeSpan.FromMilliseconds(100)));
return this;
}
/// <summary>
/// Create the Symbolic information DataAreas, DataTypes and Symbols.
/// </summary>
private SymbolicTestServer AddSymbols()
{
// Create some Primitive types
PrimitiveType dtBool = new PrimitiveType("BOOL", typeof(bool)); // 1-Byte size
PrimitiveType dtInt = new PrimitiveType("INT", typeof(short)); // 2-Byte size
PrimitiveType dtDInt = new PrimitiveType("DINT", typeof(int)); // 4-Byte size
// Create an TwinCAT specific Type PCCH (for testing purposes)
// Which is used for interop to C++ TCOM Modules
// It defines a pointer to an UTF8 String, where the Length of the data is specified
// With a LengthIs parameter
PrimitiveType dtByte = new PrimitiveType("BYTE", typeof(byte)); // Necessary for the PCCH Type (which is in fact an Alias to POINTER TO BYTE)!
PCCHType dtPCCH = PCCHType.Create();
// StringType (fixed size)
StringType dtString = new StringType(80, Encoding.Unicode);
// Struct Type
// Associated to this struct Type, we use a corresponding 'ValueType' within our ServerValues
// to hold the StructInstance value. This class/value is used to marshal/unmarshal struct types.
// See the C# class 'MyStruct' at the bottom of this example.
StructType dtStruct = new StructType("MYSTRUCT", null)
// Add Members
.AddAligned(new Member("name", dtString))
.AddAligned(new Member("a", dtBool))
.AddAligned(new Member("b", dtInt))
.AddAligned(new Member("c", dtDInt));
// Create an RPC Invokable Type
// Firstly the methods ...
// INT Method1([in] INT i1, [in] i2)
RpcMethod rpc1 = new RpcMethod("Method1")
//Add Parameters
.AddParameter("i1", dtInt, MethodParamFlags.In)
.AddParameter("i2", dtInt, MethodParamFlags.In)
//Set Return Type
.SetReturnType(dtInt);
// INT Method2([in] INT in1, [out] INT out1)
RpcMethod rpc2 = new RpcMethod("Method2")
.AddParameter("in1", dtInt, MethodParamFlags.In)
.AddParameter("out1", dtInt, MethodParamFlags.Out)
.SetReturnType(dtInt);
// STRING[80] Method3([in] INT len, [in][LengthIs = 1] PCCH str)
RpcMethod rpc3 = new RpcMethod("Method3")
.AddParameter("len", dtInt, MethodParamFlags.In)
// Referencing to parameter 'len' via LengthIs Attribute
.AddParameter("str", dtPCCH, MethodParamFlags.In, 1)
.SetReturnType(dtString);
// STRING[80] Method4([in] INT len, [out][LengthIs = 1] PCCH str)
RpcMethod rpc4 = new RpcMethod("Method4")
.AddParameter("len", dtInt, MethodParamFlags.In)
.AddParameter("str", dtPCCH, MethodParamFlags.Out, 1)
.SetReturnType(dtString);
// STRING[80] Method5([in] INT len, [out][LengthIs = 1] PCCH str)
RpcMethod rpc5 = new RpcMethod("Method5")
.AddParameter("len", dtInt, MethodParamFlags.In)
.AddParameter("str", dtPCCH, MethodParamFlags.Out, 1)
.SetReturnType(dtString);
// ... Create the RpcStructType itself and bind the Members/Methods
StructType dtStructRpc = new StructType("MYRPCSTRUCT");
dtStructRpc
// Add Members (Fields)
.AddAligned(new Member("name", dtString))
.AddAligned(new Member("a", dtBool))
.AddAligned(new Member("b", dtInt))
.AddAligned(new Member("c", dtDInt))
// Add Methods
.AddMethod(rpc1)
.AddMethod(rpc2);
// Create an Array Type
// Create the Dimensions
IDimensionCollection dims = new DimensionCollection()
// Add Dimension
.AddDimension(new Dimension(0, 4))
.AddDimension(new Dimension(0, 2));
// Create the Array Type itself
ArrayType dtArray = new ArrayType(dtInt, dims);
// Create an Enumeration Type
// Define the Enum Fields/Values
EnumValueCollection<int> enumValues = new EnumValueCollection<int>()
// Add Enumeration Fields/Entry
.AddValue("None", 0)
.AddValue("Red", 1)
.AddValue("Yellow", 2)
.AddValue("Green", 3);
// Create the EnumType
EnumType<int> dtEnum = new EnumType<int>("MYENUM", dtDInt, enumValues);
// Alias Type
AliasType dtAlias = new AliasType("MYALIAS", dtEnum);
//Pointer Type
PointerType dtPointer = new PointerType(dtInt, this.PlatformPointerSize);
// Reference Type
ReferenceType dtReference = new ReferenceType(dtInt, this.PlatformPointerSize);
// Add the Types to the internal SymbolFactory
base.symbolFactory
.AddType(dtBool)
.AddType(dtInt)
.AddType(dtDInt)
.AddType(dtString)
.AddType(dtStruct)
.AddType(dtArray)
.AddType(dtEnum)
.AddType(dtAlias)
.AddType(dtPointer)
.AddType(dtReference)
.AddType(dtStructRpc)
.AddType(dtByte)
.AddType(dtPCCH);
// Define some ProcessImage DataAreas (virtual) used for IndexGroup/IndexOffset Alignements
DataArea general = new DataArea("General", 0x01, 0x1000, 0x1000);
DataArea globals = new DataArea("Globals", 0x02, 0x1000, 0x1000);
DataArea statics = new DataArea("Statics", 0x03, 0x1000, 0x1000);
// Add the DataAreas to the SymbolFactory
base.symbolFactory
.AddDataArea(general)
.AddDataArea(globals)
.AddDataArea(statics);
// Create the Symbols (Symbol Instances)
// With DataType and define its DataArea in the (virtual)ProcessImage
base.symbolFactory
.AddSymbol("Globals.bool1", dtBool, globals)
.AddSymbol("Globals.int1", dtInt, globals)
.AddSymbol("Globals.dint1", dtDInt, globals)
.AddSymbol("Globals.string1", dtString, globals)
.AddSymbol("Globals.myStruct1", dtStruct, globals)
.AddSymbol("Globals.myArray1", dtArray, globals)
.AddSymbol("Globals.myEnum1", dtEnum, globals)
.AddSymbol("Globals.myAlias1", dtAlias, globals)
.AddSymbol("Globals.pointer1", dtPointer, globals)
.AddSymbol("Globals.reference1", dtReference, globals)
.AddSymbol("Globals.rpcInvoke1", dtStructRpc, globals)
.AddSymbol("Main.bool1", dtBool, general)
.AddSymbol("Main.int1", dtInt, general)
.AddSymbol("Main.dint1", dtDInt, general)
.AddSymbol("Main.string1", dtString, general)
.AddSymbol("Main.myStruct1", dtStruct, general)
.AddSymbol("Main.myArray1", dtArray, general)
.AddSymbol("Main.myEnum1", dtEnum, general)
.AddSymbol("Main.myAlias1", dtAlias, general)
.AddSymbol("Main.pointer1", dtPointer, general)
.AddSymbol("Main.reference1", dtReference, general)
.AddSymbol("Main.rpcInvoke1", dtStructRpc, general);
// Here we set the initial values of or Symbol instances
_symbolValues.Add(this.Symbols["Globals.bool1"], true);
_symbolValues.Add(this.Symbols["Globals.int1"], (short)42);
_symbolValues.Add(this.Symbols["Globals.dint1"], 42);
_symbolValues.Add(this.Symbols["Globals.string1"], "Hello world!");
_symbolValues.Add(this.Symbols["Globals.myStruct1"], new MyStruct("Globals.myStruct1", true, 42, 99));
_symbolValues.Add(this.Symbols["Globals.myArray1"], new short[4, 2]); ;
_symbolValues.Add(this.Symbols["Globals.myEnum1"], "Yellow");
_symbolValues.Add(this.Symbols["Globals.myAlias1"], "Red");
_symbolValues.Add(this.Symbols["Globals.pointer1"], 0);
_symbolValues.Add(this.Symbols["Globals.reference1"], 0);
_symbolValues.Add(this.Symbols["Globals.rpcInvoke1"], new MyStruct("Globals.rpcInvoke1", false, 555, 666));
_symbolValues.Add(this.Symbols["Main.bool1"], true);
_symbolValues.Add(this.Symbols["Main.int1"], (short)42);
_symbolValues.Add(this.Symbols["Main.dint1"], 42);
_symbolValues.Add(this.Symbols["Main.string1"], "Hello world!");
_symbolValues.Add(this.Symbols["Main.myStruct1"], new MyStruct("Main.myStruct1", true, 42, 99));
_symbolValues.Add(this.Symbols["Main.myArray1"], new short[4, 2]);
_symbolValues.Add(this.Symbols["Main.myEnum1"], "Yellow");
_symbolValues.Add(this.Symbols["Main.myAlias1"], "Red");
_symbolValues.Add(this.Symbols["Main.pointer1"], 0);
_symbolValues.Add(this.Symbols["Main.reference1"], 0);
_symbolValues.Add(this.Symbols["Main.rpcInvoke1"], new MyStruct("Main.rpcInvoke1", false, 555, 666));
return this;
}
/// <summary>
/// Called when an ADS Read State indication is received.
/// </summary>
/// <param name="sender">The sender address of the request.</param>
/// <param name="invokeId">The invoke identifier of the request.</param>
/// <param name="cancel">The cancellation token.</param>
/// <returns>A task that represents the asynchronous <see cref="M:TwinCAT.Ads.Server.AdsServer.ReadDeviceStateIndicationAsync(TwinCAT.Ads.AmsAddress,System.UInt32,System.Threading.CancellationToken)" /> operation. The <see cref="T:System.Threading.Tasks.Task`1" /> parameter contains the <see cref="T:TwinCAT.Ads.AdsErrorCode" /> as
/// <see cref="P:System.Threading.Tasks.Task`1.Result" />.</returns>
/// <remarks>Overwrite this method in derived classes to react on ADS Read State indications.
/// The default implementation replies with an ADS ServiceNotSupported error code (0x701).</remarks>
protected override Task<ResultReadDeviceState> OnReadDeviceStateAsync(AmsAddress sender, uint invokeId, CancellationToken cancel)
{
AdsState adsState = AdsState.Run;
ushort deviceState = 0;
StateInfo state = new StateInfo(adsState, deviceState);
ResultReadDeviceState result = ResultReadDeviceState.CreateSuccess(state);
return Task.FromResult(result);
}
/// <summary>
/// Handler function for Reading the internal value data in raw format.
/// </summary>
/// <param name="symbol">The symbol.</param>
/// <param name="span">The span.</param>
/// <returns>AdsErrorCode.</returns>
/// <remarks>This method is called, when a Read request was received to read the symbols value.
/// Implement this handler to Read and marshal the value data.</remarks>
protected override AdsErrorCode OnReadRawValue(ISymbol symbol, Span<byte> span)
{
object value;
if (_symbolValues.TryGetValue(symbol, out value))
{
int bytes = 0;
if (_symbolMarshaler.TryMarshal(symbol, value, span, out bytes))
return AdsErrorCode.NoError;
else
return AdsErrorCode.DeviceInvalidSize;
}
else
return AdsErrorCode.DeviceSymbolNotFound;
}
/// <summary>
/// Handler function for writing the symbol value data in raw format.
/// </summary>
/// <param name="symbol">The symbol.</param>
/// <param name="span">The span.</param>
/// <returns>AdsErrorCode.</returns>
/// <remarks>This method is called, when a Write request was received to overwrite the symbols value.
/// Implement this handler to overwrite the Ads Servers internal value data.</remarks>
protected override AdsErrorCode OnWriteRawValue(ISymbol symbol, ReadOnlySpan<byte> span)
{
object value;
_symbolMarshaler.Unmarshal(symbol, span, null, out value);
SetValue(symbol, value);
return AdsErrorCode.NoError;
}
/// <summary>
/// Handler function to store a new Symbol value within internal caches of the <see cref="T:TwinCAT.Ads.Server.AdsSymbolicServer" />.
/// </summary>
/// <param name="symbol">The symbol.</param>
/// <param name="value">The value.</param>
/// <param name="valueChanged">if set to <c>true</c>, the value was changed.</param>
/// <returns>AdsErrorCode.</returns>
protected override AdsErrorCode OnSetValue(ISymbol symbol, object value, out bool valueChanged)
{
object oldValue = null;
valueChanged = false;
if (_symbolValues.TryGetValue(symbol, out oldValue))
{
if (!value.Equals(oldValue))
{
valueChanged = true;
}
return AdsErrorCode.NoError;
}
return AdsErrorCode.DeviceSymbolNotFound;
}
/// <summary>
/// Handler function to determine the (stored) value of the symbol from the internal caches of the <see cref="T:TwinCAT.Ads.Server.AdsSymbolicServer" />.
/// </summary>
/// <param name="symbol">The symbol.</param>
/// <returns>System.Object.</returns>
protected override AdsErrorCode OnGetValue(ISymbol symbol, out object value)
{
if (_symbolValues.TryGetValue(symbol, out value))
{
return AdsErrorCode.NoError;
}
return AdsErrorCode.DeviceSymbolNotFound;
}
/// <summary>
/// Handler function called when an RpcInvoke occurs.
/// </summary>
/// <param name="structInstance">The structure instance.</param>
/// <param name="method">The method.</param>
/// <param name="parameterValues">The parameter values.</param>
/// <param name="returnValue">The return value.</param>
/// <returns>AdsErrorCode.</returns>
/// <remarks>Overwrite this method to react on RpcInvokes within your custom <see cref="AdsSymbolicServer" />.
/// The default implementation returns <see cref="AdsErrorCode.DeviceServiceNotSupported" />.</remarks>
protected override AdsErrorCode OnRpcInvoke(IInterfaceInstance structInstance, IRpcMethod method, object[] parameterValues, out object returnValue)
{
// Here we implement or handler for the RPC Call.
object val;
// Select the right RpcStructInstance and get its value object.
if (_symbolValues.TryGetValue(structInstance, out val))
{
MyStruct myStructValue = val as MyStruct;
if (myStructValue != null)
{
// For demo simplification, we choose the Method simply by name.
// This could be done in a more generic way, e.g with Reflection or whatever custom infrastructure.
switch (method.Name)
{
case "Method1":
{
returnValue = myStructValue.Method1((short)parameterValues[0], (short)parameterValues[1]);
return AdsErrorCode.NoError;
}
case "Method2":
{
returnValue = myStructValue.Method2((short)parameterValues[0], out var out1);
parameterValues[1] = out1;
return AdsErrorCode.NoError;
}
default:
returnValue = null;
return AdsErrorCode.DeviceServiceNotSupported;
}
}
else
{
returnValue = null;
return AdsErrorCode.DeviceServiceNotSupported;
}
}
else
{
returnValue = null;
return AdsErrorCode.DeviceServiceNotSupported;
}
}
}
// Necessary helper struct to use the .NET Default Interop marshaler which is used by the
// SymbolicAnyTypeMarshaler to Marshal/Unmarshal Struct values.
// The Layout must exactly map the 'dtMyStruct' DataType definition, so that
// the .NET Interop default marshaler is able to 'blit' the value in its own data buffers.
// MyStruct als implements the RpcInvoke Methods in this example.
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 1)]
public class MyStruct
{
// Constructor
public MyStruct(string name, bool a, short b, int c)
{
this.name = name;
this.a = a;
this.b = b;
this.c = c;
}
[FieldOffset(0)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 81)]
public string name;
[FieldOffset(162)]
[MarshalAs(UnmanagedType.U1)] // Boolean is Marshaled as UnmanagedType.I4 otherwise
public bool a;
[FieldOffset(163)]
// [MarshalAs(UnmanagedType.I2)] (Default)
public short b;
[FieldOffset(165)]
// [MarshalAs(UnmanagedType.I4)] (Default)
public int c;
// Definition of the RpcMethods
public short Method1(short i1, short i2)
{
// Just return the addition of both inputs
return (short)(i1 + i2);
}
public short Method2(short i1, out short i2)
{
i2 = (short)(i1 + 1);
return (short)(i1 + 2);
}
}