diff --git a/.copier-answers.yml b/.copier-answers.yml index e233aca..a4b4022 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -6,6 +6,4 @@ _commit: v1.1.2 _src_path: https://github.com/bec-project/plugin_copier_template.git make_commit: false project_name: csaxs_bec -widget_plugins_input: -- name: csaxs_test - use_ui: true +widget_plugins_input: [] diff --git a/csaxs_bec/devices/epics/mcs_card/mcs_card_csaxs.py b/csaxs_bec/devices/epics/mcs_card/mcs_card_csaxs.py index 35ff0a3..25958c9 100644 --- a/csaxs_bec/devices/epics/mcs_card/mcs_card_csaxs.py +++ b/csaxs_bec/devices/epics/mcs_card/mcs_card_csaxs.py @@ -119,8 +119,8 @@ class MCSCardCSAXS(PSIDeviceBase, MCSCard): ) self._mcs_clock = 1e7 # 10MHz clock -> 1e7 Hz self._pv_timeout = 3 # TODO remove timeout once #129 in ophyd_devices is solved - self._rlock = RLock() - self.counter_mapping = { + self._rlock = RLock() # Needed to ensure thread safety for counter updates + self.counter_mapping = { # Any mca counter that should be updated has to be added here f"{self.counters.name}_mca1": "current1", f"{self.counters.name}_mca2": "current2", f"{self.counters.name}_mca3": "current3", @@ -166,35 +166,67 @@ class MCSCardCSAXS(PSIDeviceBase, MCSCard): sig.subscribe(self._on_counter_update, run=False) def _on_counter_update(self, value, **kwargs) -> None: + """ + Callback for counter updates of the mca channels (1-32). + + The raw data is pushed to the mcs sub-device (MCSRaw). We need to ensure that + the MCSRaw device has all signals defined for which we want to push the values. + + As we may receive multiple readings per point, e.g. if frames_per_trigger > 1, + we also create a mean value for the counter signals. These are then pushed to the bpm device + for plotting and further processing. The signal names are defined and mapped in the + self.counter_mapping dictionary & the bpm sub-device. + + There are multiple mca channels, each giving individual updates. We want to ensure that + each is updated before we signal that we are ready to read. In future, these signals may + become asynchronous, but we first need to ensure that we can properly combine monitored + signals with async signals for plotting. Until then, we will keep this logic. + """ with self._rlock: + # Retrieve the signal object which executes this callback signal = kwargs.get("obj", None) - if signal is None: + if signal is None: # This should never happen, but just in case logger.info(f"Called without 'obj' in kwargs: {kwargs}") return + # Get the maped signal name from the mapping dictionary mapped_signal_name = self.counter_mapping.get(signal.name, None) + # If we did not map the signal name in counter_mapping, but receive an update + # we will skip it. if mapped_signal_name is None: return + # Push the raw values of the mca channels. The signal name has to be defined + # in the self.mcs sub-device (MCSRaw) to be able to push the values. Otherwise + # we will skip the update. mca_raw = getattr(self.mcs, signal.name.split("_")[-1], None) if mca_raw is None: return + # In case there was more than one value received, i.e. frames_per_trigger > 1, + # we will receive a np.array of values. if isinstance(value, np.ndarray): + # We push the raw values as a list to the mca_raw signal + # And otherwise compute the mean value for plotting of counter signals mca_raw.put(value.tolist()) + # compute the count_time in seconds if mapped_signal_name == "count_time": value = value / self._mcs_clock value = float(value.mean()) else: + # We received a single value, so we can directly push it mca_raw.put(value) + # compute the count_time in seconds if mapped_signal_name == "count_time": value = value / self._mcs_clock - # Mean signal for burst acquisition + # Get the mapped signal from the bpm device and update it sig = getattr(self.bpm, mapped_signal_name) sig.put(value) self.counter_updated.append(signal.name) + # Once all mca channels have been updated, we can signal that we are ready to read received_all_updates = set(self.counter_updated) == set(self.counter_mapping.keys()) if received_all_updates: - self.ready_to_read.put(READYTOREAD.DONE) # Reset happens from DDG class! - self.counter_updated.clear() + self.ready_to_read.put(READYTOREAD.DONE) + # The reset of the signal is done in the on_trigger method of ddg1 for the next trigger + self.counter_updated.clear() # Clear the list for the next update cycle def _progress_update(self, value, **kwargs) -> None: """Callback for progress updates from ophyd subscription on current_channel.""" diff --git a/test.txt b/test.txt deleted file mode 100644 index df9fe2d..0000000 --- a/test.txt +++ /dev/null @@ -1 +0,0 @@ -jjakjda