stlehmann / pyads

Python wrapper for TwinCAT ADS
MIT License
253 stars 94 forks source link

Timeout when write value on notification callback #107

Closed kryskool closed 3 years ago

kryskool commented 4 years ago

HI

I try to update variable on a notification, but i received an " ADSError: timeout elapsed (1861)." each time i receive one.

my sample code

VAR_GLOBAL

    CC_CODE_VAL                 : STRING(20);
    CC_TRIG_UP                  : BOOL;
    CC_ACK_UP                   : BOOL;

END_VAR

With this code

TC_AMS_ID = cfg.get('twincat', 'ams_id')
TC_ADDR_ID = cfg.get('twincat', 'addr_id')
TC_ADDR_PORT = int(cfg.get('twincat', 'port'))
TC_USER = cfg.get('twincat', 'user')
TC_PASS = cfg.get('twincat', 'passwd')

pyads.open_port()
adr = pyads.AmsAddr(TC_AMS_ID, TC_ADDR_PORT)
pyads.add_route(adr, TC_ADDR_ID

plc = pyads.Connection(TC_AMS_ID, TC_ADDR_PORT, TC_ADDR_ID)
plc.open()

@plc.notification(pyads.PLCTYPE_BOOL)
def callback(handle, name, timestamp, value):
    print(
        '{1}: received new notification for variable "{0}", value: {2}'
        .format(name, timestamp, value)
    )
    plc.write_by_name('.CC_CODE_VAL', 'OK', pyads.PLCTYPE_STRING)
    plc.write_by_name('.CC_TRIG_UP', True, pyads.PLCTYPE_BOOL)

try:
    print('>> Add CC_ACK_UP notification')
    handles1 = plc.add_device_notification(
      '.CC_ACK_UP', pyads.NotificationAttrib(1, max_delay=0.1), callback)
except pyads.pyads_ex.ADSError as e:
    print('ADS error when registrer device notification')
    print(e)
    sys.exit(2)

while True:
    pass

i receive this error code on start

2019-12-06 09:02:03.293000: received new notification for variable ".CC_ACK_UP", value: False
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 232, in 'calling callback function'
  File "/usr/local/lib/python3.7/dist-packages/pyads/pyads_ex.py", line 788, in wrapper
    return callback(notification, data_name)
  File "/usr/local/lib/python3.7/dist-packages/pyads/ads.py", line 1059, in func_wrapper
    return func(contents.hNotification, data_name, dt, value)
  File "test-ads.py", line 72, in callback
    plc.write_by_name('.CC_CODE_VAL', 'OK', pyads.PLCTYPE_STRING)
  File "/usr/local/lib/python3.7/dist-packages/pyads/ads.py", line 898, in write_by_name
    self._port, self._adr, data_name, value, plc_datatype, handle=handle
  File "/usr/local/lib/python3.7/dist-packages/pyads/pyads_ex.py", line 726, in adsSyncWriteByNameEx
    handle = adsGetHandle(port, address, data_name)
  File "/usr/local/lib/python3.7/dist-packages/pyads/pyads_ex.py", line 658, in adsGetHandle
    PLCTYPE_STRING,
  File "/usr/local/lib/python3.7/dist-packages/pyads/pyads_ex.py", line 542, in adsSyncReadWriteReqEx2
    raise ADSError(err_code)
pyads.pyads_ex.ADSError: ADSError: timeout elapsed (1861).

when i put True on CC_ACK_UP

2019-12-06 09:02:21.403000: received new notification for variable ".CC_ACK_UP", value: True
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 232, in 'calling callback function'
  File "/usr/local/lib/python3.7/dist-packages/pyads/pyads_ex.py", line 788, in wrapper
    return callback(notification, data_name)
  File "/usr/local/lib/python3.7/dist-packages/pyads/ads.py", line 1059, in func_wrapper
    return func(contents.hNotification, data_name, dt, value)
  File "test-ads.py", line 72, in callback
    plc.write_by_name('.CC_CODE_VAL', 'OK', pyads.PLCTYPE_STRING)
  File "/usr/local/lib/python3.7/dist-packages/pyads/ads.py", line 898, in write_by_name
    self._port, self._adr, data_name, value, plc_datatype, handle=handle
  File "/usr/local/lib/python3.7/dist-packages/pyads/pyads_ex.py", line 726, in adsSyncWriteByNameEx
    handle = adsGetHandle(port, address, data_name)
  File "/usr/local/lib/python3.7/dist-packages/pyads/pyads_ex.py", line 658, in adsGetHandle
    PLCTYPE_STRING,
  File "/usr/local/lib/python3.7/dist-packages/pyads/pyads_ex.py", line 542, in adsSyncReadWriteReqEx2
    raise ADSError(err_code)
pyads.pyads_ex.ADSError: ADSError: timeout elapsed (1861).

can we change variable value directly on notification callback ?

Regards

wyda commented 4 years ago

I had the same issue. Not sure why it is like this. What i did is just putting the read/write into a separte thread. Maybe it's not the best solution but worked out for my case.

Something like this:

def write():
     plc.write_by_name('.CC_CODE_VAL', 'OK', pyads.PLCTYPE_STRING)
     plc.write_by_name('.CC_TRIG_UP', True, pyads.PLCTYPE_BOOL)

@plc.notification(pyads.PLCTYPE_BOOL)
def callback(handle, name, timestamp, value):
    print(
        '{1}: received new notification for variable "{0}", value: {2}'
        .format(name, timestamp, value)
     )
    t = Thread(target=write)
    t.start()
stlehmann commented 4 years ago

@kryskool is this still an issue?

kryskool commented 4 years ago

Hi @stlehmann

I use a similar hack as @wyda proposal with Thread + Queue module, but with the decorator @plc.notification we cannot use the same connection to write value until we leave the function

I don't know if it's a bug, but with parallel processing it works

Regards

stlehmann commented 4 years ago

OK so the connection seems to be blocking while a notification callback is processed. I'm not sure what the reason is, though. It could be the ADS DLL blocking or it could be the Python implementation. I think this needs further investigation.

stlehmann commented 4 years ago

Just had a look at the ADS documentation:

Efficient Usage

  • Don't use time intensive executions in callbacks.
  • Remind to sync your callback and your mainthread, if you access each other (e.g. critical sections, mutex, events).

It is a bit confusing, though. First point suggests that the calls are blocking. The second points suggests that it is threaded.

stlehmann commented 4 years ago

@pbruenn do you maybe have any insights how notifications are handled internally by the ADS library?

pbruenn commented 4 years ago

Do you use TcAdsDll.dll provided by TwinCAT? Or do you use the open source AdsLib? I have no insight into TwinCATs TcAdsDll.dll and the open source library should handle this asynchronously: https://github.com/Beckhoff/ADS/blob/b21b93b79346627adcc99d37163cad709087ed3f/AdsLib/NotificationDispatcher.cpp#L58

stlehmann commented 4 years ago

I just confirmed this behaviour for a Windows machine which uses the TcAdsDLL.dll provided by TwinCAT. I'll give it a try with the AdsLib also.

kryskool commented 4 years ago

Hi @pbruenn

I use AdsLib (come from pyads) on Raspberry Pi 3

Regards,

stlehmann commented 3 years ago

@kryskool This issue should be fixed with version 3.2.2. So I'm closing this issue by now. Specifically a bug in adslib led to the timeout. If there are still any troubles feel free to reopen.

kryskool commented 3 years ago

@stlehmann

Thanks, can point me the commit that fix this issue ?

Regards,

stlehmann commented 3 years ago

Yes, it should be this one: 45859d6e9038b55d319efdbda95d3d6eeadd45e3