diff --git a/ophyd_devices/sim/sim_signals.py b/ophyd_devices/sim/sim_signals.py index 588603d..a9c1b16 100644 --- a/ophyd_devices/sim/sim_signals.py +++ b/ophyd_devices/sim/sim_signals.py @@ -209,7 +209,20 @@ class ReadOnlySignal(Signal): self._readback = self._value = self._get_value() else: self._readback = np.random.rand() - self._run_subs(sub_type=self.SUB_VALUE, old_value=old_value, value=self._readback) + try: + if ( + isinstance(old_value, (int, float, list)) and old_value != self._readback + ): # only run subs if the value has changed + self._run_subs(sub_type=self.SUB_VALUE, old_value=old_value, value=self._readback) + else: # must be numpy + if not np.array_equal(old_value, self._readback): + self._run_subs( + sub_type=self.SUB_VALUE, old_value=old_value, value=self._readback + ) + except Exception as e: + logger.info( + f"Error in comparing old_value {old_value} with new_value {self._readback}: {e}" + ) return self._readback # pylint: disable=arguments-differ diff --git a/ophyd_devices/utils/dynamic_pseudo.py b/ophyd_devices/utils/dynamic_pseudo.py index a1034b5..aace960 100644 --- a/ophyd_devices/utils/dynamic_pseudo.py +++ b/ophyd_devices/utils/dynamic_pseudo.py @@ -76,6 +76,7 @@ class ComputedSignal(SignalRO): self._signal_subs = [] self._compute_method = None self._compute_method_str = None + self._active_callbacks: set[str] = set() def _signal_callback(self, *args, **kwargs): old_value = self._readback @@ -172,3 +173,22 @@ class ComputedSignal(SignalRO): return_value = self._compute_method() self._readback = return_value if return_value is not None else self._readback return return_value + + def _run_subs(self, *args, sub_type, **kwargs): + """ + This method runs the callbacks for a given subscription type. It is overridden to ensure that + callbacks for the same subscription type can not trigger additional subscriptions of the same type. + We thereby avoid that callbacks can triggered recursively. In practice, a callback may call 'get' + or 'read' itself, but it won't trigger any recursive calls of the callbacks for the same subscription type. + + Args: + sub_type (str): The subscription type for which to run the callbacks. + """ + if sub_type in self._active_callbacks: + return + try: + self._active_callbacks.add(sub_type) + super()._run_subs(*args, sub_type=sub_type, **kwargs) + finally: + if sub_type in self._active_callbacks: + self._active_callbacks.remove(sub_type)