stlehmann / pyads

Python wrapper for TwinCAT ADS
MIT License
257 stars 95 forks source link

Reading TwinCAT UNION types #388

Closed janwilch closed 4 months ago

janwilch commented 4 months ago

I'm trying to use read_structure_by_name to read a STRUCT containing a UNION from the PLC. It seems this does not work properly because pyads interprets the structure_def as purely consecutive bytes, when the bytes on the PLC are in fact "overlapping" (I'm not sure about the exact data structure).

I would be very grateful if you could look into that and maybe have some suggestion for me!

TwinCAT type definitions

TYPE UValues :
UNION
    Binary: BOOL;
    Numeric: LREAL;
    String_t: STRING;
END_UNION
END_TYPE

TYPE EDataType :
(
    none := 0,
    dont_care := 1,
    binary := 2,
    numeric := 3,
    string_t := 4
) UINT;
END_TYPE

TYPE SValue :
STRUCT
    DataType: EDataType;
    Value: UValues;
    Defined: BOOL;
    Name: STRING;
END_STRUCT
END_TYPE

Python code

async def ads_read():
    ads = pyads.Connection(
        ams_net_id="5.34.12.243.1.1",
        ams_net_port=pyads.PORT_TC3PLC1
    )

    named_value_def = (
        ("DataType", pyads.PLCTYPE_UINT, 1),
        ("Value.Binary", pyads.PLCTYPE_BOOL, 1),    # 1 byte
        ("Value.Numeric", pyads.PLCTYPE_LREAL, 1),  # 8 bytes
        ("Value.String_t", pyads.PLCTYPE_STRING, 1, 80),    # 80 bytes
        ("Defined", pyads.PLCTYPE_BOOL, 1),
        ("Name", pyads.PLCTYPE_STRING, 1, 80)
    )

    with ads:
        while True:
            res_dict = ads.read_structure_by_name("myVal", named_value_def)
            for name, val in res_dict.items():
                print(f"{(name + ':').ljust(60, ' ')}{val}")
            await asyncio.sleep(1.5)

Expected output

DataType:                                                   3
Value.Binary:                                               False
Value.Numeric:                                              1.432861019581777e+214
Value.String_t:                                             lo world
Defined:                                                    False
Name:                                                       

Actual output

DataType:                                                   3
Value.Binary:                                               # invalid / doesn't matter
Value.Numeric:                                              8.7657764788278537e+228      # doesn't matter either
Value.String_t:                                             Hello world
Defined:                                                    True
Name:                                                       my value
chrisbeardy commented 4 months ago

Hello, thank you for your question. In order to assist with issue management, please keep the issue tracker reserved for bugs and feature requests. For any questions, particularly around usage, route creation and ads error messages when reading or writing variables, please use Stack Overflow tagging the question with twincat-ads and state you are using the pyads library. Thanks.

However, to answer your question here, I am not sure what you want to do is supported, as you say the structure definition wants consecutive bytes therefore the way you have defined it assumes you have a bool, and a lreal and a string one byte aligned. Structures must also be declared this way in the PLC. I am also not sure how the PLC represents a UNION of these 3 types underneath, you may find that it takes the largest of the datatypes, therefore you could just declare your structure def in python as only a string? You may have to experiment or if possible change to not read a structure. I am also not sure if just reading a union as read_by_name what you would get either...

I am closing this issue here and please ask if you need further support to post on stack overflow. Thanks.