From b1c920819ee0bdeea33a4eb5db5a7ad269dadf76 Mon Sep 17 00:00:00 2001 From: Markus Zolliker Date: Wed, 7 May 2025 14:15:53 +0200 Subject: [PATCH] frappy.client.interactive: improve updates while driving - instead to show first current 'value' and 'status', and then the changes, show changes only - this way updates appear in the expected order - for this SecopClient.register_callback needs a 'callimmediately' argument Change-Id: I3e91c2c15bca7fee2eba3b1bf1dd27313da3ae29 Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/36291 Reviewed-by: Markus Zolliker Tested-by: Jenkins Automated Tests --- frappy/client/__init__.py | 20 ++++++++++++-------- frappy/client/interactive.py | 19 +++++++++---------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/frappy/client/__init__.py b/frappy/client/__init__.py index 99d8dca..ad69a83 100644 --- a/frappy/client/__init__.py +++ b/frappy/client/__init__.py @@ -209,16 +209,20 @@ class ProxyClient: # caches (module, parameter) = value, timestamp, readerror (internal names!) self.cache = Cache() # dict returning Cache.undefined for missing keys - def register_callback(self, key, *args, callimmediately=None, **kwds): + def register_callback(self, key, *args, callimmediately=True, **kwds): """register callback functions - - key might be either: + several callbacks might be registered within one call. + ProxyClient.CALLBACK_NAMES contains all names of valid callbacks + + :param key: might be either: 1) None: general callback (all callbacks) 2) : callbacks related to a module (not called for 'unhandledMessage') - 3) (, ): callback for specified parameter (only called for 'updateEvent') - - all the following arguments are callback functions. The callback name may be - given by the keyword, or, for non-keyworded arguments it is taken from the - __name__ attribute of the function + 3) (, ): callback for specified parameter + (only called for 'updateEvent' and 'updateItem') + :param args: callback functions. the callback name is taken from the the __name__ attribute of the function + :param callimmediately: True (default): call immediately for updateItem and updateEvent callbacks + :param kwds: callback functions. the callback name is taken from the key """ for cbfunc in args: kwds[cbfunc.__name__] = cbfunc @@ -226,8 +230,8 @@ class ProxyClient: if cbname not in self.CALLBACK_NAMES: raise TypeError(f"unknown callback: {', '.join(kwds)}") - # immediately call for some callback types - if cbname in ('updateItem', 'updateEvent') and callimmediately is not False: + # call immediately for some callback types + if cbname in ('updateItem', 'updateEvent') and callimmediately: if key is None: # case generic callback cbargs = [(m, p, d) for (m, p), d in self.cache.items()] else: diff --git a/frappy/client/interactive.py b/frappy/client/interactive.py index 21a1100..4cd377d 100644 --- a/frappy/client/interactive.py +++ b/frappy/client/interactive.py @@ -143,7 +143,7 @@ class Module: def _isBusy(self): return self.status[0] // 100 == StatusType.BUSY // 100 - def _status_value_update(self, m, p, status, t, e): + def _status_update(self, m, p, status, t, e): if self._is_driving and not self._isBusy(): self._is_driving = False self._driving_event.set() @@ -216,10 +216,11 @@ class Module: def __call__(self, target=None): if target is None: return self.read() - for pname in 'value', 'status': + watch_params = ['value', 'status'] + for pname in watch_params: self._secnode.register_callback((self._name, pname), - callimmediately=False, - updateEvent=self._watch_parameter) + updateEvent=self._watch_parameter, + callimmediately=False) self.target = target # this sets self._is_driving @@ -239,11 +240,10 @@ class Module: pass clientenv.raise_with_short_traceback(e) finally: - # self._watch_parameter(self._name, 'status') self._secnode.readParameter(self._name, 'value') - # self._watch_parameter(self._name, 'value', forced=True) - self._secnode.unregister_callback((self._name, 'value'), updateEvent=self._watch_parameter) - self._secnode.unregister_callback((self._name, 'status'), updateEvent=self._watch_parameter) + for pname in watch_params: + self._secnode.unregister_callback((self._name, pname), + updateEvent=self._watch_parameter) return self.value def __repr__(self): @@ -418,8 +418,7 @@ class Client(SecopClient): attrs[cname] = Command(cname, modname, self) mobj = type(f'M_{modname}', (Module,), attrs)(modname, self) if 'status' in mobj._parameters: - self.register_callback((modname, 'status'), updateEvent=mobj._status_value_update) - self.register_callback((modname, 'value'), updateEvent=mobj._status_value_update) + self.register_callback((modname, 'status'), updateEvent=mobj._status_update) clientenv.namespace[modname] = mobj if removed_modules: self.log.info('removed modules: %s', ' '.join(removed_modules))