Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Beckhoff Linux Distro: Connection closed by remote #433

Open
Danko-tbg opened this issue Nov 25, 2024 · 6 comments
Open

Beckhoff Linux Distro: Connection closed by remote #433

Danko-tbg opened this issue Nov 25, 2024 · 6 comments

Comments

@Danko-tbg
Copy link

Hi,

I am running the Linux distribution provided by Beckhoff, which includes the TwinCAT runtime and the ADS router. I am trying to use the PyADS library to communicate with either the local runtime or another TwinCAT runtime on a Windows system.

In both cases, there is already an ADS route present. However, I am struggling to get PyADS to work as expected. After reviewing the PyADS documentation and troubleshooting, I suspect the issue might be related to how routes are handled on Linux, given that the Beckhoff distribution already has an integrated ADS router.

The PyADS documentation states:
For Linux systems the route is created automatically on the client-side. For the target-side, you can use add_route_to_plc().

Since a route is already present, could this be the reason for my issues?

image

This is my Python code

import pyads
import time

TARGET_NETID = "127.0.0.1.1.1"  

print(f"Attempting to connect to PLC at AMS Net ID: {TARGET_NETID}, Port: {pyads.PORT_TC3PLC1}")

try:
    with pyads.Connection(TARGET_NETID, pyads.PORT_TC3PLC1) as plc:
        print("Connection established successfully.")

        
        print("Checking PLC status...")
        state, device_state = plc.read_state()
        print(f"PLC State: {state}, Device State: {device_state}")

        if state != pyads.ADSSTATE_RUN:
            print("PLC is not in RUN state. Exiting...")
            exit(1)
        else:
            print("PLC is in RUN state. Proceeding to read variables...")

        while True:
            try:
                # Read the value of MAIN.bOutput_1
                print("Reading value of 'MAIN.bOutput_1'...")
                variable = plc.read_by_name('MAIN.bOutput_1', pyads.PLCTYPE_BOOL)
                print(f"Current value of 'MAIN.bOutput_1': {variable}")

                # Wait for 1 second before next read
                time.sleep(1)

            except pyads.ADSError as e:
                print(f"Error reading 'MAIN.bOutput_1': {e}")
                break

except pyads.ADSError as e:
    print(f"Failed to connect or communicate with PLC: {e}")
except KeyboardInterrupt:
    print("Script interrupted by user.")
finally:
    print("Exiting script.")

If I remove the route from the Linux machine, and only have it on the Windows machine, I instead get the following error.
(10.200.238.2.1.1 is the AMS Net id to my Windows machine).
image

But from my development computer, we can clearly see that the target port exists (same code)
image

@chrisbeardy
Copy link
Collaborator

Hi, the documentation you are referring too assumes that you are using a linux client to access a Windows Beckhoff PLC and that the Linux client has no ADS router (e.g. not the Beckhoff distribution). The Beckhoff Linux system is very new and pyads has never been tested with it (so this is an interesting test), it also means that the documentation predates the Beckhoff Linux distribution. I would have liked to think if you are running the pyads on the same system as the linux PLC then it would just work with localhost and you wouldn't need to create any routes.

I'm slighty confused by your 3 screenshots: is the first screenshot you running pyads on the Beckhoff Linux PC? Is the second screenshot you running pyads on a Windows machine with PLC running on Linux? and the third screenshot you running both pyads and the PLC on a windows machine?

I wouldn’t be surpirsed if there is some issue with pyads connecting correctly to a Beckhoff Linux PLC as it stands and the code would probably be trying to access its own ADS router type functionalty which may cause issues. It may be an easy fix though...

@Danko-tbg
Copy link
Author

Thanks for a quick reply - and sorry for the confusion. Let me clarify.

I would have liked to think if you are running the pyads on the same system as the linux PLC then it would just work with localhost and you wouldn't need to create any routes.
For local communication, there is a built-in loopback route, which I was referring to. I did not need to create a route when running locally, only externally

Screenshot 1:
PyADS on Beckhoff Linux distro <-> local TwinCAT runtime.
No route created by me, but a built-in loopback should be present as I stated earlier.
Since it actually connects in this case, the loopback should be there.
The question is why it gets "closed by remote".

Screenshot 2: (Two tests, same result therefore only one screenshot)
Test 1:
PyADS on Beckhoff Linux distro <-> external W10 TwinCAT runtime.
Route created on W10 via TwinCAT Route GUI but only on the W10 machine
(Since the documentation stated that PyADS creates the route). Using the following setting.
I cleared the StaticRoutes.XML and restarted before doing this test.
image

Test 2:
PyADS on Beckhoff Linux distro <-> external W10 TwinCAT runtime.
Route created on W10 via TwinCAT Route GUI with a two-way route (both on "target" and "remote" as Beckhoff calls it).
image

Screenshot 3:
PyADS on my W11 laptop in PyCharm <-> same external W10 TwinCAT runtime as in screenshot 2.
Route created normally as we usually do with the route GUI. (The screenshot from "Test 2" above).
This was just to verify that the code was actually working on Windows.

Screenshot 4:
image
I made a new test now (essentially screenshot 2, Test 1) but adding the ip adress in the pyads.connection method as well.
pyads.Connection(TARGET_NETID, pyads.PORT_TC3PLC1,"10.200.233.39") as plc:

It could clearly connect this time (so route is created by PyADS), but connection closed by remote is still thrown.

@chrisbeardy
Copy link
Collaborator

we see that connection closed by remote error on linux currently when we try to connect more than once (see discussion #422 ). I wonder if this is related as due to the ADS library / version we are using, I wonder if the ADS proxy fix will work here? take a look at that discussion and try a few things. It may also be worth seeing if you can change the pyads code locally to use the linux "dll" on the linux machine instead of the one we bundle, if that works we will have to change our code to recognise when its running on a beckhoff linux distribution.

@chrisbeardy
Copy link
Collaborator

did any of this help?

@Danko-tbg
Copy link
Author

We are testing some stuff currently - we could see the same behavior in some other libraries as well, so maybe not related to the PyADS implementation. I will update any findings asap.

@Danko-tbg
Copy link
Author

Made some new tests. I am really not familiar with neither Linux or ADS so please bear with me.
I havnt tested the ADSproxy yet, but I tested editing the pyads_ex.py code to use the local ADS ".dll".

elif platform_is_linux():
    # Explicitly load the library libTcAdsDll.so
    adslib = "/usr/lib/libTcAdsDll.so"

    if not os.path.isfile(adslib):
        raise FileNotFoundError(f"Cannot find library at {adslib}")

    _adsDLL = ctypes.CDLL(adslib)

    NOTEFUNC = ctypes.CFUNCTYPE(
        None,
        ctypes.POINTER(SAmsAddr),
        ctypes.POINTER(SAdsNotificationHeader),
        ctypes.c_ulong,
    )

Which threw the error message

Unexpected error: /usr/lib/libTcAdsDll.so: undefined symbol: AdsAddRoute

I exported the functions available in libTcAdsDll.so

(myenv) Administrator@BTN-000t2usj:~/src$ nm -D /usr/lib/libTcAdsDll.so
0000000000017be0 T AdsAmsPortEnabled
00000000000181f0 T AdsAmsPortEnabledEx
0000000000017b60 T AdsAmsRegisterRouterNotification
0000000000018130 T AdsAmsRegisterRouterNotificationEx2
0000000000017b90 T AdsAmsUnRegisterRouterNotification
00000000000181a0 T AdsAmsUnRegisterRouterNotificationEx2
00000000000176b0 T AdsGetDllVersion
0000000000017790 T AdsGetLocalAddress
0000000000017c40 T AdsGetLocalAddressEx
0000000000017730 T AdsPortClose
0000000000017c30 T AdsPortCloseEx
0000000000017720 T AdsPortOpen
0000000000017c20 T AdsPortOpenEx
0000000000017a90 T AdsSyncAddDeviceNotificationReq
0000000000017fc0 T AdsSyncAddDeviceNotificationReqEx
0000000000017b00 T AdsSyncDelDeviceNotificationReq
0000000000018060 T AdsSyncDelDeviceNotificationReqEx
0000000000017bb0 T AdsSyncGetTimeout
00000000000180f0 T AdsSyncGetTimeoutEx
00000000000179c0 T AdsSyncReadDeviceInfoReq
0000000000017e80 T AdsSyncReadDeviceInfoReqEx
0000000000017830 T AdsSyncReadReq
0000000000017870 T AdsSyncReadReqEx
0000000000017d30 T AdsSyncReadReqEx2
0000000000017a50 T AdsSyncReadStateReq
0000000000017f60 T AdsSyncReadStateReqEx
00000000000178e0 T AdsSyncReadWriteReq
0000000000017940 T AdsSyncReadWriteReqEx
0000000000017dd0 T AdsSyncReadWriteReqEx2
0000000000017b30 T AdsSyncSetTimeout
00000000000180b0 T AdsSyncSetTimeoutEx
0000000000017a00 T AdsSyncWriteControlReq
0000000000017ee0 T AdsSyncWriteControlReqEx
00000000000177f0 T AdsSyncWriteReq
0000000000017cb0 T AdsSyncWriteReqEx

Maybe it is not the right file, but it is the only one I could find containing "adsdll".

(myenv) Administrator@BTN-000t2usj:~$ sudo find / -type f -iname '*AdsDll*' 2>/dev/null
/usr/lib/libTcAdsDll.so

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants