ni / nidaqmx-python

A Python API for interacting with NI-DAQmx
Other
440 stars 154 forks source link

dash background callback not working with nidaqmx #557

Closed 2WG73 closed 6 months ago

2WG73 commented 6 months ago

Ubuntu 18.04. Tested with Python 3.10.9 nidaqmx 0.9.0

I am using multiprocessing together with plotly-dash and nidaqmx. I am also using Background callback from dash.

I have the following Instruments class:

class Instruments :

      def __init__(self, ch_analog):
              self.analog_task = nidaqmx.Task()
              self.analog_task.ao_channels.add_ao_voltage_chan(ch_analog, min_val=0, max_val=0.04)
              self.analog_task.start()

      def __del__(self):
              self.analog_task.close()

          def read_write_stop_digital_output(self, write = False, stop = False):
              self.analog_task.write(0.02 if write else 0)
              if stop == True:
                  self.analog_task.stop()
                  self.analog_task.close()
              return none

      def run(self):
              p = mp.Process(target=self.read_write_stop_digital_output, args=())
              p.start()
              print('Instruments_O ' + str(p.pid) + ', Parent ' + str(os.getppid()))

I instantiate the class in the main process :

# import need libraries 
# I only show the import of waitresss
from waitress import serve
# Note that I also import _class Instruments_ 

# Dash Background callback
launch_uid = uuid4()
cache = diskcache.Cache("./cache")
background_callback_manager = DiskcacheManager(
    cache, cache_by=[lambda: launch_uid], expire=60,
)

app = dash.Dash(
    __name__,
    external_stylesheets=[dbc.themes.COSMO],
    background_callback_manager=background_callback_manager,
    title='Test',
    prevent_initial_callbacks="initial_duplicate")

server = app.server

# Define callbacks (not shown) and Layout (non shown)

# Instruments digital IO
instruments = MT_devices.digitalIO("Dev1/ao0")    
instruments.run()

if __name__ == '__main__':
    print('Dash server about to start...')
    serve(app.server, host='127.0.0.1', port=8050)

Here, my background callback does not work.

If, however, I comment

instruments_O = Instruments("Dev1/ao0")    
instruments_O.run()

it works.

If, however, I use a standard callback (not a background callback) together with

instruments_O = MT_devices.digitalIO("Dev1/ao0")    
instruments_O.run()

it works.

zhindes commented 6 months ago

DAQmx tasks aren't shareable across processes. Looking at your instrument class, it appears you're relying on that. The Instruments class creates the task in __init__. Then, later, you spawn a separate Process to handle the write operations of that task, which doesn't exist in the other process. If you want this to work, I'd suggest moving all of the DAQ configuration into the Process method.

2WG73 commented 6 months ago

Makes sense ! Thanks

2WG73 commented 6 months ago

Thanks a lot