OPCFoundation / UA-.NETStandard

OPC Unified Architecture .NET Standard
Other
1.94k stars 943 forks source link

Get strongly typed result from ExtensionObject.Body #2509

Closed janbiehl closed 7 months ago

janbiehl commented 7 months ago

Type of issue

Current Behavior

I am currently trying to read a complex type from the server. To do this, I first loaded the TypeSystem from the server. This also seems to have worked, as I can also see in the body of the ExtensionObject that the value is of type "DT_PlaceData" which is the name of the S7-1500 PLC UDT.

I would now like to be able to access it without having to do so via the body of an ExtensionObject. It should be a typed result. Unfortunately I can't find a way to convert the body to my type.

Debugger_View

I am reading the value as following, in general this is a wrapper around the ISession.ReadValueAsync method. var value = await client.ReadNodeValueAsync("ns=4;i=5");

This is how the custom type is defined. The Attributes are not official ones, i plan to use it for register single types for the Complex Type system. As of now i am loading any types from the server. So these should not have any effect right now.

[OpcType("ns=4;i=2")]
[OpcTypeEncoding("ns=4;i=3")]
public sealed class DtPlaceVisu
{
    public LocalizedText Name { get; set; }
    public short OperationMode { get; set; }
    public short State { get; set; }
    public bool Running { get; set; }
    public bool Occupied { get; set; }
}

Expected Behavior

Is there a way to get a strongly typed "DtPlaceVisu" out of the ExtensionsObjects.Body. Or is there maybe some other way to retrieve a typed result directly out of the ISession.ReadValueAsync method.

Steps To Reproduce

No response

Environment

- OS: [Windows 11, macOS]
- Environment: Jetbrains Rider 2023.3
- Runtime: .NET 8.0
- Nuget Version: 1.5.373.121
- Component: Opc.UaClient, Opc.UaClient.ComplexTypes
- Server: S7-1500 PLC SimAdvanced 5.0
- Client: Custom

Anything else?

No response

janbiehl commented 7 months ago

Tonight I was wondering if the reference to the TypeSystem has to be used to read values from the session.

I currently load the TypeSystem from the server and save the reference in the client. I still don't use this reference at all.

Is that perhaps the problem? Does the TypeSystem have to be included in the value reading process somehow?

janbiehl commented 7 months ago

This is how i load the type system from server

    public static async Task<ComplexTypeSystem> LoadTypeSystemAsync(ISession session,
        CancellationToken cancellationToken = default)
    {
        using var activity = Telemetry.ActivitySource.StartActivity();

        try
        {
            ArgumentNullException.ThrowIfNull(session);
            ComplexTypeSystem typeSystem = new(session);

            var watch = Stopwatch.StartNew();

            await typeSystem.Load(false, true, cancellationToken)
                .ConfigureAwait(false);

            watch.Stop();

            activity?.SetTag("LoadTime", watch.ElapsedMilliseconds);
            activity?.SetTag("TypesLoaded", typeSystem.GetDefinedTypes().Length);

            return typeSystem;
        }
        catch (Exception e)
        {
            activity?.RecordException(e);
            throw;
        }
    }
janbiehl commented 7 months ago

Hey, I think the problem has been solved. I did some research and discovered the IComplexTypeProperties interface. This now gives me access to the values that make up the complex type.

Can anyone confirm that this is a valid way to map the complex type to its own class?

    public static DtPlaceVisu Create(IComplexTypeProperties complexType)
    {
        var value = new DtPlaceVisu
        {
            Name = (string) complexType[nameof(DtPlaceVisu.Name)],
            OperationMode = (short) complexType[nameof(DtPlaceVisu.OperationMode)],
            State = (short) complexType[nameof(DtPlaceVisu.State)],
            Running = (bool) complexType[nameof(DtPlaceVisu.Running)],
            Occupied = (bool) complexType[nameof(DtPlaceVisu.Occupied)],
        };

        return value;
    }