ophyd icon indicating copy to clipboard operation
ophyd copied to clipboard

EpicsMCA Ophyd Device reports triggering from sucess before it's complete after staging

Open whs92 opened this issue 2 years ago • 0 comments

Problem was seen when trying to use the EpicsMCA ophyd device with a scan plan. The mca device is a Bruker/Rontec.

RE(count([mca]))

results in the device being staged, and triggered, and then immediately reading, rather than waiting for the status object of the trigger method to report sucess.

If the trigger method is called outside of the RE without staging like mca.trigger() then the status object reports done correctly once the record has finished processing.

If the device is first staged, and then triggered, then the status object returned by the trigger() method already reports sucess.

Removing everything from mca.stage_sigs and mca.configuration_attrs does not fix the problem.

Here is the output with trace messages from the runengin

17:18:45.738100 stage             -> bruker          args: (), kwargs: {}, run: None
17:18:45.739458 open_run          -> None            args: (), kwargs: {'detectors': ['bruker'], 'num_points': 1, 'num_intervals': 0, 'plan_args': {'detectors': ["EpicsMCA(prefix='SISSY2EX:SDD00:mca1', name='bruker', read_attrs=['preset_real_time', 'elapsed_real_time', 'spectrum'], configuration_attrs=[])"], 'num': 1}, 'plan_name': 'count', 'hints': {'dimensions': [(('time',), 'primary')]}}, run: None


Transient Scan ID: 1     Time: 2021-10-04 17:18:45
Persistent Unique Scan ID: 'a00b7e32-987f-443d-a778-0fea556edcc8'
17:18:45.745888 checkpoint        -> None            args: (), kwargs: {}, run: None
17:18:45.746267 checkpoint        -> None            args: (), kwargs: {}, run: None
17:18:45.746700 trigger           -> bruker          args: (), kwargs: {'group': 'trigger-36101d'}, run: None
17:18:45.747916 wait              -> None            args: (), kwargs: {'group': 'trigger-36101d'}, run: None
17:18:45.748928 create            -> None            args: (), kwargs: {'name': 'primary'}, run: None
17:18:45.749229 read              -> bruker          args: (), kwargs: {}, run: None
17:18:45.751733 save              -> None            args: (), kwargs: {}, run: None
New stream: 'primary'
+-----------+------------+
|   seq_num |       time |
+-----------+------------+
|         1 | 17:18:45.7 |
17:18:45.755403 close_run         -> None            args: (), kwargs: {'exit_status': None, 'reason': None}, run: None
+-----------+------------+
generator count ['a00b7e32'] (scan num: 1)



17:18:45.858398 unstage           -> bruker          args: (), kwargs: {}, run: None

('a00b7e32-987f-443d-a778-0fea556edcc8',)

I have a workaround for the moment which rewrites the trigger method to consider other fields than just the callback from the erase_start signal.

class MyEpicsMCA(EpicsMCA):
    
    #redefine erase_start so that it's no longer in self.trigger_signals
    erase_start = Cpt(EpicsSignal, 'EraseStart', kind='omitted')
    
    #Add the PV that monitors when acquisition is complete
    acquiring = Cpt(EpicsSignalRO, '.ACQG', kind='omitted')
        
    def trigger(self):
        
        callback_signal = self.acquiring
        #variable used as an event flag
        acquisition_status = False
           
        def acquisition_started(status):
            nonlocal acquisition_status #Define as nonlocal as we want to modify it
            acquisition_status = True
                
        def check_value(*, old_value, value, **kwargs):
            #Return True when the acquisition is complete, False otherwise.
                                   
            if not acquisition_status:  #But only report done if acquisition was already started
                
                return False
                       
            return (value == 0 and old_value == 1 and self.preset_real_time.get() == self.elapsed_real_time.get())
        
        # create the status with SubscriptionStatus that add's a callback to check_value.
        sta_cnt = SubscriptionStatus(callback_signal, check_value, run=False, settle_time = 3) #Add some settle time
         
        # Start the acquisition
        sta_acq = self.erase_start.set(1)
        
        sta_acq.add_callback(acquisition_started)
        
        stat = AndStatus(sta_cnt, sta_acq)
        
        return stat

whs92 avatar Oct 04 '21 15:10 whs92