stlehmann / pyads

Python wrapper for TwinCAT ADS
MIT License
247 stars 93 forks source link

Reconnect after a "Failed to open port on AMS router." error #267

Open Podmornica opened 2 years ago

Podmornica commented 2 years ago

If I try to connect and TwinCAT has not completely started then I get the error "Failed to open port on AMS router.". After I receive this error I can not establish the connection even after TwinCAT has started. I tried to call again plc.open(), but I always get the same error. If I restart the entire program after TwinCAT had started then the connection is working.

Is there a correct way to retry a connection after you receive "Failed to open port on AMS router."?

P.S.: TwinCAT and the program is running on the same machine which is running Windows 10.

chrisbeardy commented 2 years ago

Hello, have you tried recreating the instance of the plc Connection? e.g. redoing plc = pyads.Connection(...).

A workaround for this is to wait for twincat to start before starting the python program. TwinCat provides a useful mechanism for this, if you place a shortcut to your program (or batch file which starts it) in the follwing folder, TwinCat will launch it automatically for you after it has started

C:\TwinCAT\3.1\Target\StartUp

Podmornica commented 2 years ago

Hello!

Yes I tried to recreate the instance, but I always get the same error "Failed to open port on AMS router.". I also tried to wait a couple of seconds and recreate the instance, but the results are always the same. The only thing that helps is to restart the entire program.

P.S.: Thank you for the workaround tip, but I would like to understand why the error is persisting and if it is solvable with some code.

chrisbeardy commented 2 years ago

I've been looking into this and I think it may be an issue the underlying TwinCat ADS C Dll. As pyads just calls this when using plc.open(). We could log an issue with https://github.com/Beckhoff/ADS.

I believe the entire ADS Dll will need to be reloaded on the event of an error. Even if you delete the entire pyads library refernece (https://stackoverflow.com/a/487718) untill the ref count for "pyads" is only 3, it still fails after you reimport pyads and try to do plc.open(). I think this is because python still keeps the c dll in memory (https://stackoverflow.com/questions/359498/how-can-i-unload-a-dll-using-ctypes-in-python). Hence the only fix is restarting the program/intepreter.

The workaround may be the best solution, or another workaround would be to check programitically the TwinCat3 System Service is running before trying to connect for the first time. image

Possibly using https://stackoverflow.com/questions/33843024/check-if-a-windows-service-exists-with-python.

Unloading an reloading a C DLL in python does not look trivial from my first searches https://stackoverflow.com/questions/359498/how-can-i-unload-a-dll-using-ctypes-in-python

stlehmann commented 2 years ago

@Podmornica Just to be sure: Did you try: Closing the connection with plc.close() and reconnecting with plc.open(). This normally works for me if a connection got lost.

Otherwise I agree with @chrisbeardy and ask you to file an issue to https://github.com/Beckhoff/ADS.

chrisbeardy commented 2 years ago

@stlehmann not sure the connection being lost is an issue here as no connection is made in the first place, as the TC system service has not started the DLL can't connect (open the port) to the TwinCat AMS router on the local pc. It then gets stuck until the dll is started again which python only does on restart of the interpreter.

chrisbeardy commented 2 years ago

You can replicate the error by stopping the service on the pc and trying to connect. Doing this using plc.close() then plc.open() does not clear the error

stlehmann commented 2 years ago

I can't see so many use-cases where TwinCAT is not started up before a Python client makes connection. In any case this needs to be passed to https://github.com/Beckhoff/ADS as we can not tackle it from our side.

stlehmann commented 2 years ago

@pbruenn have you already had this issue in your repository?

pbruenn commented 2 years ago

Well, I know people want connection handling in AdsLib, but this particular case "attempt TCP connection before TwinCAT is up", is new to me. I never had the time to implement connection handling for long running applications, but in order to reconnect the TCP connection it should be sufficient to call void DelLocalRoute(AmsNetId ams); for all the routes you opened with long AddLocalRoute(AmsNetId ams, const char* ip); If you already use the AdsDevice object this should happen at end of the objects lifetime(https://github.com/Beckhoff/ADS/blob/master/AdsLib/AdsDevice.cpp#L20). So the first thing I would check is does the python wrapper close all his connections? And if so is the connection list really empty? https://github.com/Beckhoff/ADS/blob/master/AdsLib/standalone/AmsRouter.cpp#L69

Only if that code was run a new TCP connection will be attempt: https://github.com/Beckhoff/ADS/blob/master/AdsLib/standalone/AmsRouter.cpp#L47

Sorry, but at the moment I have no resources to look deeper into it myself. But if you can debug this, keep me informed I try to support as good as I can, while traveling.

stlehmann commented 2 years ago

@pbruenn thanks for the feedback on this matter. We'll keep this issue open for now and see if we can figure out where the problem is exactly located.