From 205215bf0bea169db374aee90ba4de5a17ad9a86 Mon Sep 17 00:00:00 2001 From: Aleksander Sil Date: Mon, 6 Oct 2025 11:38:21 +0200 Subject: [PATCH 1/4] fix: fix plc_variable register_notification method not using notification_count as static variable --- src/pyads/testserver/advanced_handler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pyads/testserver/advanced_handler.py b/src/pyads/testserver/advanced_handler.py index 7ba51877..92a1b6cf 100644 --- a/src/pyads/testserver/advanced_handler.py +++ b/src/pyads/testserver/advanced_handler.py @@ -164,9 +164,9 @@ def write(self, value: bytes, request: AmsPacket = None): def register_notification(self) -> int: """Register a new notification.""" - handle = self.notification_count + handle = PLCVariable.notification_count self.notifications.append(handle) - self.notification_count += 1 + PLCVariable.notification_count += 1 return handle def unregister_notification(self, handle: int = None): From 4b80cf4b4df259ba4ce65dbca94383dab5ded83e Mon Sep 17 00:00:00 2001 From: Aleksander Sil Date: Mon, 6 Oct 2025 13:24:15 +0200 Subject: [PATCH 2/4] docs: added note in changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 361b03a5..88eccda2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed * [#400](https://github.com/stlehmann/pyads/issues/400) Full support for pyproject.toml +### Fixed +* [#471](https://github.com/stlehmann/pyads/issues/471) Fixed registering of multiple notifications on AdsTestServer + ## 3.5.0 ### Added From 428f0db69e1bd539f0e7dcc8be60c41385881032 Mon Sep 17 00:00:00 2001 From: Aleksander Sil Date: Mon, 6 Oct 2025 13:44:51 +0200 Subject: [PATCH 3/4] test: add test for fix of issue #471 --- tests/test_testserver.py | 90 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/tests/test_testserver.py b/tests/test_testserver.py index 20b3cada..2a797234 100644 --- a/tests/test_testserver.py +++ b/tests/test_testserver.py @@ -10,7 +10,7 @@ import time import unittest import pyads -from pyads.testserver import AdsTestServer, BasicHandler +from pyads.testserver import AdsTestServer, BasicHandler, AdvancedHandler, PLCVariable # These are pretty arbitrary TEST_SERVER_AMS_NET_ID = "127.0.0.1.1.1" @@ -85,5 +85,93 @@ def test_server_disconnect_then_del_device_notification(self): self.fail(f"Closing server connection raised: {e}") + def test_advanced_handler_register_multiple_notifications(self): + """Test registering multiple notifications on the same variable. + + Tests fix of issue [#471](https://github.com/stlehmann/pyads/issues/471), original pull request: [#474](https://github.com/stlehmann/pyads/pull/474) + """ + handler = AdvancedHandler() + test_server = AdsTestServer(handler=handler, logging=False, ip_address=TEST_SERVER_IP_ADDRESS) + + var1 = "MAIN.var1" + var2 = "MAIN.var2" + + # Create two WORD variables in the handler, with explicit indices + handler.add_variable( + PLCVariable( + var1, + 0, + symbol_type="WORD", + ads_type=pyads.constants.ADST_UINT16, + index_group=61445, + index_offset=10000, + ) + ) + + handler.add_variable( + PLCVariable( + var2, + 0, + symbol_type="WORD", + ads_type=pyads.constants.ADST_UINT16, + index_group=61445, + index_offset=10001, + ) + ) + + test_server.start() + time.sleep(0.1) + + plc = pyads.Connection(TEST_SERVER_AMS_NET_ID, TEST_SERVER_AMS_PORT, TEST_SERVER_IP_ADDRESS) + plc.open() + + try: + # Collect notifications + events = [] + + @plc.notification(pyads.PLCTYPE_UINT) + def on_change(handle, name, timestamp, value): + events.append((name, value)) + + attrib = pyads.NotificationAttrib( + length=2, + trans_mode=pyads.ADSTRANS_SERVERONCHA, + max_delay=0, + cycle_time=0, + ) + + handles1 = plc.add_device_notification(var1, attrib, on_change) + handles2 = plc.add_device_notification(var2, attrib, on_change) + + # Trigger notifications + triggers = [ + (var1, 456), + (var2, 4321), + (var1, 789), + ] + for var, value in triggers: + plc.write_by_name(var, value, pyads.PLCTYPE_UINT) + + time.sleep(0.2) + + # Cleanup notifications + if handles1 is not None: + plc.del_device_notification(*handles1) + if handles2 is not None: + plc.del_device_notification(*handles2) + + # Assert we observed expected changes + values_by_name = {} + for name, value in events: + values_by_name.setdefault(name, []).append(value) + + for var, value in triggers: + self.assertIn(value, values_by_name[var]) + finally: + plc.close() + test_server.stop() + time.sleep(0.1) + + if __name__ == "__main__": unittest.main() From 549542a09583ce0c00b73ad4aaae0993b204eadd Mon Sep 17 00:00:00 2001 From: Aleksander Sil Date: Tue, 7 Oct 2025 10:25:36 +0200 Subject: [PATCH 4/4] test: change way of adding variables and notifications to variables in test_testserver.py --- tests/test_testserver.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/test_testserver.py b/tests/test_testserver.py index 2a797234..f0ee0762 100644 --- a/tests/test_testserver.py +++ b/tests/test_testserver.py @@ -102,9 +102,7 @@ def test_advanced_handler_register_multiple_notifications(self): var1, 0, symbol_type="WORD", - ads_type=pyads.constants.ADST_UINT16, - index_group=61445, - index_offset=10000, + ads_type=pyads.constants.ADST_UINT16 ) ) @@ -114,8 +112,6 @@ def test_advanced_handler_register_multiple_notifications(self): 0, symbol_type="WORD", ads_type=pyads.constants.ADST_UINT16, - index_group=61445, - index_offset=10001, ) ) @@ -129,6 +125,9 @@ def test_advanced_handler_register_multiple_notifications(self): # Collect notifications events = [] + sym1 = plc.get_symbol(var1) + sym2 = plc.get_symbol(var2) + @plc.notification(pyads.PLCTYPE_UINT) def on_change(handle, name, timestamp, value): events.append((name, value)) @@ -140,8 +139,11 @@ def on_change(handle, name, timestamp, value): cycle_time=0, ) - handles1 = plc.add_device_notification(var1, attrib, on_change) - handles2 = plc.add_device_notification(var2, attrib, on_change) + # Adding notifications using index_group and index_offset from the symbols + # as using name "add_variable" without explicit indices would create indices (12345, 10000 + some offset) + # and add_device_notification expects (61445, 10000 + some offset) in this case. + handles1 = plc.add_device_notification((sym1.index_group, sym1.index_offset), attrib, on_change) + handles2 = plc.add_device_notification((sym2.index_group, sym2.index_offset), attrib, on_change) # Trigger notifications triggers = [