11-05-2024 08:36 AM
Hello!
I've originally posted this issue on the nidaqmx-python github (https://github.com/ni/nidaqmx-python/issues/637). I was advised by zhindes to post here.
Here goes the original message:
Hello!
I've been having this problem for some time now, but I finally managed to recreate it reliably. Before, I would get it randomly, with multiple days without it, and then some days getting it multiple times.
Below code runs for a couple of seconds before windows crashes with a blue screen of death:
import nidaqmx
import nidaqmx.stream_readers
import numpy as np
device = nidaqmx.system.System().local().devices[0]
task = nidaqmx.Task("analog_input")
task.ai_channels.add_ai_voltage_chan(
physical_channel=",".join([device.ai_physical_chans.channel_names[0], device.ai_physical_chans.channel_names[1]]),
name_to_assign_to_channel="joystick",
terminal_config=nidaqmx.constants.TerminalConfiguration.DIFF
)
task.ai_channels.add_ai_voltage_chan(
physical_channel=device.ai_physical_chans[3].name,
name_to_assign_to_channel="touch",
terminal_config=nidaqmx.constants.TerminalConfiguration.RSE
)
task.timing.cfg_samp_clk_timing(rate=150, sample_mode=nidaqmx.constants.AcquisitionType.CONTINUOUS)
def _analog_samples_acquired_callback(task_handle, every_n_samples_event_type,
number_of_samples, callback_data):
try:
analog_reader.read_many_sample(np.empty((1, 3)), number_of_samples, timeout=0) # Value not needed for demonstration
except nidaqmx.DaqReadError as daq_read_error:
# Check if task has already been stopped. If not, reraise error (else ignore).
if not task.is_task_done():
raise daq_read_error
return -1 # return value of 1 indicates that error was ignored
return 0
analog_reader = nidaqmx.stream_readers.AnalogMultiChannelReader(task.in_stream)
task.register_every_n_samples_acquired_into_buffer_event(1, _analog_samples_acquired_callback)
try:
while True:
print("START")
task.start()
print("STOP")
task.stop()
finally:
print("DONE!")
task.close()
The BSOD check code is either a 0x00000139 (KERNEL_SECURITY_CHECK_FAILURE) or 0x00000044 (MULTIPLE_IRP_COMPLETE_REQUESTS). Which one it is seems to be random (at least I don't know what causes the difference).
My original code was stopping and immediately restarting the task regularly. This is to ensure synchronization of the recording start time with an external signal, which happens every 4 to 9 seconds.
I'm attaching the contents of my 'C:\Windows\Minidump' folder as zip archive.
Any ideas on what the problem might be?
Thank you!
Suggestions by zhindes that I have tried, to no avail:
Windows is not in a VM. The card is connected to a 'Intel(R) USB 3.1 eXtensible-Hostcontroller - 1.10 (Microsoft)'. A screenshot of the corresponding section in the device manager can be found below.
I have also tried running the same script on another, more powerful computer, with the same outcome. But I've only gotten 'KERNEL_SECURITY_CHECK_FAILED' errors in about 10 tries. I can also give additional info on that machine if needed.
Thank you!
06-03-2025 08:47 AM
Hello dnzrgn,
Have you been able to solve the problem? I have similar crashes, but I don't use Python for data acquisition but Labview.
Many thanks in advance
06-04-2025 02:36 AM - edited 06-04-2025 02:38 AM
Hi MCF_Patrick,
I abandoned the callback function registration via
register_every_n_samples_acquired_into_buffer_event
entirely. Instead, I created my own python thread just to read the analog data into my recordings array. I don't know if that's any help for you since you are using LabView, sorry.
For anyone stumbling upon this in the future: For my usecase (relatively low input frequency), using a python thread is sufficient. Though you have to jump through some hoops to make it work. Below is a sample code. BUT, I had to reduce the code and remove sensitive information. I currently don't have access to the hardware, so I cannot test if this sample code is working. It's just to convey the general idea. Use at your own risk.
import nidaqmx
import nidaqmx.stream_readers
import numpy as np
import threading
from time import sleep
INPUT_FREQ = 150, # [Hz]
INPUT_SAMPLE_INTERVAL = 1 # INPUT_FREQ / INPUT_SAMPLE_INTERVAL = SAMPLE_FREQ [Hz]
MAX_RECORDING_TIME = 120*60, # [sec]
READ_AI_TIMEOUT = 2, # [sec]
device = nidaqmx.system.System().local().devices[0]
task = nidaqmx.Task("analog_input")
task.ai_channels.add_ai_voltage_chan(
physical_channel=",".join([device.ai_physical_chans.channel_names[0], device.ai_physical_chans.channel_names[1]]),
name_to_assign_to_channel="joystick",
terminal_config=nidaqmx.constants.TerminalConfiguration.DIFF
)
task.ai_channels.add_ai_voltage_chan(
physical_channel=device.ai_physical_chans[3].name,
name_to_assign_to_channel="touch",
terminal_config=nidaqmx.constants.TerminalConfiguration.RSE
)
task.timing.cfg_samp_clk_timing(rate=150, sample_mode=nidaqmx.constants.AcquisitionType.CONTINUOUS)
_ai_in_stream = task.in_stream
_buffer_ai = np.empty((task.number_of_channels, INPUT_SAMPLE_INTERVAL), dtype=np.float64)
_recording_size = 150*(MAX_RECORDING_TIME + (60 * 5))
_recording = np.empty((task.number_of_channels, _recording_size), dtype=np.float64)
_index_recording = 0
def _job_ai(lock: threading.Lock, stop_event: threading.Event):
global _index_recording
reader = nidaqmx.stream_readers.AnalogMultiChannelReader(_ai_in_stream)
new_sample = False
while True:
if stop_event.is_set():
stop_event.clear()
break
# Give main thread chance to acquire lock
sleep(0.001)
lock.acquire()
try:
if task.is_grabbing:
reader.read_many_sample(_buffer_ai, INPUT_SAMPLE_INTERVAL, timeout=READ_AI_TIMEOUT)
new_sample = True
finally:
lock.release()
if new_sample:
_recording[..., _index_recording:_index_recording+INPUT_SAMPLE_INTERVAL] = _buffer_ai[:]
_index_recording += INPUT_SAMPLE_INTERVAL
new_sample = False
def _decorate_task_start_stop_to_use_lock(task: nidaqmx.Task, lock: threading.Lock):
"""Function that decorates given task. Decorated method acquires given lock while calling the start and stop methods of the task."""
from functools import wraps
from types import MethodType
task.is_grabbing = False
inner_start = task.start
inner_stop = task.stop
@wraps(inner_start)
def start_with_lock(self):
lock.acquire()
try:
inner_start()
self.is_grabbing = True
finally:
lock.release()
@wraps(inner_stop)
def stop_with_lock(self):
lock.acquire()
try:
inner_stop()
self.is_grabbing = False
finally:
lock.release()
task.start = MethodType(start_with_lock, task)
task.stop = MethodType(stop_with_lock, task)
_event_stop_ai = threading.Event()
_lock_ai = threading.Lock()
_thread_ai = threading.Thread(target=_job_ai, args=(_lock_ai, _event_stop_ai), daemon=True)
_decorate_task_start_stop_to_use_lock(task, _lock_ai)
task.start()
_thread_ai.start()
try:
while True:
print("START")
task.start()
print("STOP")
task.stop()
finally:
_event_stop_ai.set()
task.close()
print("DONE!")
I may have overcomplicated it a little bit. But since it was working, I was content with it.
The idea is to use a lock to ensure that the task is not started or stopped while data is being read from the buffer, and vice versa.
It was also working on a normal office laptop. So if you don't have any other resource intensive processes running on the computer, it should be possible to increase the input frequency quite a bit.
Best