From 0f854b1c77aa0298e1fa15db2c5d74d523ea4e42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Margueritat?= Date: Fri, 17 Nov 2023 17:12:56 +0100 Subject: [PATCH 01/57] new simple counter of all type --- .../plugins_0D/daq_0Dviewer_DAQmx_counter.py | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py new file mode 100644 index 0000000..eb345f3 --- /dev/null +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py @@ -0,0 +1,141 @@ +import numpy as np +from pymodaq.utils.daq_utils import ThreadCommand +from pymodaq.utils.data import DataWithAxes, DataToExport, DataSource +from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters, main +from pymodaq.utils.parameter import Parameter + +from pymodaq_plugins_daqmx.hardware.national_instruments.daqmx import DAQmx, \ + Edge, ClockSettings, Counter, ClockCounter, TriggerSettings + +from PyDAQmx import DAQmx_Val_ContSamps + +class DAQ_0DViewer_DAQmx_counter(DAQ_Viewer_base): + """ + Plugin for a 0D PL counter, based on a NI card. + """ + params = comon_parameters+[ + {"title": "Counting channel:", "name": "counter_channel", + "type": "list", "limits": DAQmx.get_NIDAQ_channels(source_type="Counter")}, + {"title": "Acq. Time (s):", "name": "acq_time", + "type": "float", "value": 1., "default": 1.}, + {'title': 'Clock channel:', 'name': 'clock_channel', 'type': 'list', + 'limits': DAQmx.get_NIDAQ_channels(source_type='Counter')} + ] + + def ini_attributes(self): + self.controller = None + self.clock_channel = None + self.counter_channel = None + self.live = False # True during a continuous grab + self.counting_time = 0.1 + + def commit_settings(self, param: Parameter): + """Apply the consequences of a change of value in the detector settings + + Parameters + ---------- + param: Parameter + A given parameter (within detector_settings) whose value has been changed by the user + """ + if param.name() == "acq_time": + self.counting_time = param.value() + else: + self.stop() + self.update_tasks() + + def ini_detector(self, controller=None): + """Detector communication initialization + + Parameters + ---------- + controller: (object) + custom object of a PyMoDAQ plugin (Slave case). None if only one actuator/detector by controller + (Master case) + + Returns + ------- + info: str + initialized: bool + False if initialization failed otherwise True + """ + self.controller = {"clock": DAQmx(), "counter": DAQmx()} + try: + self.update_tasks() + initialized = True + info = "NI card based counter" + self.counting_time = self.settings.child("acq_time").value() + except Exception as e: + print(e) + initialized = False + info = "Error" + + self.dte_signal_temp.emit(DataToExport(name='PL', + data=[DataWithAxes(name='Counts', data=[np.array([0])], + source=DataSource['raw'], + dim='Data0D', labels=['counts (Hz)'])])) + + return info, initialized + + def close(self): + """Terminate the communication protocol""" + self.controller["clock"].close() + self.controller["counter"].close() + + def grab_data(self, Naverage=1, **kwargs): + """Start a grab from the detector + + Parameters + ---------- + Naverage: int + Number of hardware averaging not relevant here. + kwargs: dict + others optionals arguments + """ + update = True # to decide if we do the initial set up or not + + if 'live' in kwargs: + if kwargs['live'] == self.live and self.live: + update = False # we are already live + self.live = kwargs['live'] + + if update: + self.update_tasks() + self.controller["clock"].start() + + read_data = self.controller["counter"].readCounter(1, counting_time=self.counting_time) + data_pl = read_data/self.counting_time # convert to kcts/s + self.dte_signal.emit(DataToExport(name='PL', + data=[DataWithAxes(name='PL', data=[data_pl], + source=DataSource['raw'], + dim='Data0D', labels=['PL (kcts/s)'])])) + + def stop(self): + """Stop the current grab hardware wise if necessary""" + self.close() + self.emit_status(ThreadCommand('Update_Status', ['Acquisition stopped.'])) + return '' + + def update_tasks(self): + """Set up the counting tasks in the NI card.""" + # Create channels + self.clock_channel = ClockCounter(1/self.settings.child("acq_time").value(), + name=self.settings.child("clock_channel").value(), + source="Counter") + self.counter_channel = Counter(name=self.settings.child("counter_channel").value(), + source="Counter", edge=Edge.names()[0]) + + self.controller["clock"].update_task(channels=[self.clock_channel], + clock_settings=ClockSettings(), + trigger_settings=TriggerSettings()) + self.controller["clock"].task.CfgImplicitTiming(DAQmx_Val_ContSamps, 1) + + self.controller["counter"].update_task(channels=[self.counter_channel], + clock_settings=ClockSettings(), + trigger_settings=TriggerSettings()) + + # connect the clock to the counter + self.controller["counter"].task.SetSampClkSrc("/" + self.clock_channel.name + "InternalOutput") + + +if __name__ == '__main__': + main(__file__) From 5d37aa4252316f56317da840d7a221cafcd279e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Margueritat?= Date: Sat, 18 Nov 2023 18:17:42 +0100 Subject: [PATCH 02/57] Update daq_0Dviewer_DAQmx_counter.py --- .../plugins_0D/daq_0Dviewer_DAQmx_counter.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py index eb345f3..3c00b4d 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py @@ -103,11 +103,11 @@ def grab_data(self, Naverage=1, **kwargs): self.controller["clock"].start() read_data = self.controller["counter"].readCounter(1, counting_time=self.counting_time) - data_pl = read_data/self.counting_time # convert to kcts/s - self.dte_signal.emit(DataToExport(name='PL', - data=[DataWithAxes(name='PL', data=[data_pl], + data = read_data/self.counting_time # convert to cts/s + self.dte_signal.emit(DataToExport(name='Counts', + data=[DataWithAxes(name='Counts', data=[data], source=DataSource['raw'], - dim='Data0D', labels=['PL (kcts/s)'])])) + dim='Data0D', labels=['Counts (Hz)'])])) def stop(self): """Stop the current grab hardware wise if necessary""" From 730c40d75fb8cd58a3c2b04c8747dbdb81d42de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Margueritat?= Date: Tue, 21 Nov 2023 16:06:55 +0100 Subject: [PATCH 03/57] using nidaqmx --- .../plugins_0D/daq_0Dviewer_DAQmx_counter.py | 36 +- .../hardware/national_instruments/_nidaqmx.py | 20 + .../national_instruments/counter_test.py | 19 + .../hardware/national_instruments/daqmxNI.py | 781 ++++++++++++++++++ 4 files changed, 842 insertions(+), 14 deletions(-) create mode 100644 src/pymodaq_plugins_daqmx/hardware/national_instruments/_nidaqmx.py create mode 100644 src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_test.py create mode 100644 src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxNI.py diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py index 3c00b4d..22082c6 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py @@ -19,7 +19,9 @@ class DAQ_0DViewer_DAQmx_counter(DAQ_Viewer_base): {"title": "Acq. Time (s):", "name": "acq_time", "type": "float", "value": 1., "default": 1.}, {'title': 'Clock channel:', 'name': 'clock_channel', 'type': 'list', - 'limits': DAQmx.get_NIDAQ_channels(source_type='Counter')} + 'limits': DAQmx.get_NIDAQ_channels(source_type='Counter')}, + {"title": "Clk Trig:", "name": "trig", + "type": "list", "limits": DAQmx.getTriggeringSources()} ] def ini_attributes(self): @@ -39,9 +41,7 @@ def commit_settings(self, param: Parameter): """ if param.name() == "acq_time": self.counting_time = param.value() - else: - self.stop() - self.update_tasks() + self.update_tasks() def ini_detector(self, controller=None): """Detector communication initialization @@ -69,17 +69,18 @@ def ini_detector(self, controller=None): initialized = False info = "Error" - self.dte_signal_temp.emit(DataToExport(name='PL', - data=[DataWithAxes(name='Counts', data=[np.array([0])], - source=DataSource['raw'], - dim='Data0D', labels=['counts (Hz)'])])) + #self.dte_signal_temp.emit(DataToExport(name='Counts', + # data=[DataWithAxes(name='Counts', data=[np.array([0])], + # source=DataSource['raw'], + # dim='Data0D', labels=['counts (Hz)'])])) return info, initialized def close(self): """Terminate the communication protocol""" - self.controller["clock"].close() - self.controller["counter"].close() + pass + #self.controller["clock"].close() + #self.controller["counter"].close() def grab_data(self, Naverage=1, **kwargs): """Start a grab from the detector @@ -91,13 +92,20 @@ def grab_data(self, Naverage=1, **kwargs): kwargs: dict others optionals arguments """ - update = True # to decide if we do the initial set up or not + update = False # to decide if we do the initial set up or not + if 'live' in kwargs: - if kwargs['live'] == self.live and self.live: + if kwargs['live'] != self.live: + update = True + self.live = kwargs['live'] + """ + if 'live' in kwargs: + if kwargs['live'] == self.live: update = False # we are already live self.live = kwargs['live'] - + """ + if update: self.update_tasks() self.controller["clock"].start() @@ -119,7 +127,7 @@ def update_tasks(self): """Set up the counting tasks in the NI card.""" # Create channels self.clock_channel = ClockCounter(1/self.settings.child("acq_time").value(), - name=self.settings.child("clock_channel").value(), + name=self.settings.child("trig").value(), source="Counter") self.counter_channel = Counter(name=self.settings.child("counter_channel").value(), source="Counter", edge=Edge.names()[0]) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/_nidaqmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/_nidaqmx.py new file mode 100644 index 0000000..e1f1ec6 --- /dev/null +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/_nidaqmx.py @@ -0,0 +1,20 @@ +import nidaqmx +from nidaqmx.constants import AcquisitionType,Edge +import time +import matplotlib.pyplot as plt + +task=nidaqmx.Task() +task.ci_channels.add_ci_count_edges_chan("Dev1/ctr0") + + +task.start() +data=task.read(512) +#for i in range(512): +# mesure0 = task.read() +# data.append(task.read()-mesure0) +task.stop() + +print(data) +task.close() +plt.plot(data) +plt.show() \ No newline at end of file diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_test.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_test.py new file mode 100644 index 0000000..11e474a --- /dev/null +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_test.py @@ -0,0 +1,19 @@ +import PyDAQmx +import ctypes +import numpy as np + + +counter_input = PyDAQmx.Task() +read = PyDAQmx.int32() +data = np.zeros((1000,),dtype=np.float64) + +#DAQmx Configure Code +counter_input.CreateCICountEdgesChan("Dev1/ctr0","",PyDAQmx.DAQmx_Val_Rising,0,PyDAQmx.DAQmx_Val_CountUp) + +#DAQmx Start Code +counter_input.StartTask() + +#DAQmx Read Code +counter_input.ReadCounterScalarU32(10.0,PyDAQmx.byref(data),None) + +print(read.value) \ No newline at end of file diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxNI.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxNI.py new file mode 100644 index 0000000..7ce5fe1 --- /dev/null +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxNI.py @@ -0,0 +1,781 @@ +import PyDAQmx +import ctypes +from enum import IntEnum +import numpy as np +from pymodaq.utils.logger import set_logger, get_module_name + +logger = set_logger(get_module_name(__file__)) + + +class DAQ_NIDAQ_source(IntEnum): + """ + Enum class of NIDAQ_source + + =============== ========== + **Attributes** **Type** + *Analog_Input* int + *Counter* int + =============== ========== + """ + Analog_Input = 0 + Counter = 1 + Analog_Output = 2 + Digital_Output = 3 + Digital_Input = 4 + Terminals = 5 + + @classmethod + def names(cls): + return [name for name, member in cls.__members__.items()] + + +class DAQ_analog_types(IntEnum): + """ + Enum class of Ai types + + =============== ========== + **Attributes** **Type** + =============== ========== + """ + Voltage = PyDAQmx.DAQmx_Val_Voltage + Current = PyDAQmx.DAQmx_Val_Current + Thermocouple = PyDAQmx.DAQmx_Val_Temp_TC + + @classmethod + def names(cls): + return [name for name, member in cls.__members__.items()] + + @classmethod + def values(cls): + return [cls[name].value for name, member in cls.__members__.items()] + + +class DAQ_thermocouples(IntEnum): + """ + Enum class of thermocouples type + + =============== ========== + **Attributes** **Type** + =============== ========== + """ + J = PyDAQmx.DAQmx_Val_J_Type_TC + K = PyDAQmx.DAQmx_Val_K_Type_TC + N = PyDAQmx.DAQmx_Val_N_Type_TC + R = PyDAQmx.DAQmx_Val_R_Type_TC + S = PyDAQmx.DAQmx_Val_S_Type_TC + T = PyDAQmx.DAQmx_Val_T_Type_TC + B = PyDAQmx.DAQmx_Val_B_Type_TC + E = PyDAQmx.DAQmx_Val_E_Type_TC + + @classmethod + def names(cls): + return [name for name, member in cls.__members__.items()] + + +class DAQ_termination(IntEnum): + """ + Enum class of thermocouples type + + =============== ========== + **Attributes** **Type** + =============== ========== + """ + Auto = PyDAQmx.DAQmx_Val_Cfg_Default + RSE = PyDAQmx.DAQmx_Val_RSE + NRSE = PyDAQmx.DAQmx_Val_NRSE + Diff = PyDAQmx.DAQmx_Val_Diff + Pseudodiff = PyDAQmx.DAQmx_Val_PseudoDiff + + @classmethod + def names(cls): + return [name for name, member in cls.__members__.items()] + + +class Edge(IntEnum): + """ + """ + Rising = PyDAQmx.DAQmx_Val_Rising + Falling = PyDAQmx.DAQmx_Val_Falling + + @classmethod + def names(cls): + return [name for name, member in cls.__members__.items()] + + +class ClockMode(IntEnum): + """ + """ + Finite = PyDAQmx.DAQmx_Val_Rising + Continuous = PyDAQmx.DAQmx_Val_Falling + + @classmethod + def names(cls): + return [name for name, member in cls.__members__.items()] + + +class ClockSettingsBase: + def __init__(self, Nsamples=1000, repetition=False): + + self.Nsamples = Nsamples + self.repetition = repetition + + +class ClockSettings(ClockSettingsBase): + def __init__(self, source=None, frequency=1000, Nsamples=1000, edge=Edge.names()[0], repetition=False): + super().__init__(Nsamples, repetition) + self.source = source + assert edge in Edge.names() + self.frequency = frequency + self.edge = edge + + +class ChangeDetectionSettings(ClockSettingsBase): + def __init__(self, Nsamples=1000, rising_channel='', falling_channel='', + repetition=False): + super().__init__(Nsamples, repetition) + self.rising_channel = rising_channel + self.falling_channel = falling_channel + + +class TriggerSettings: + def __init__(self, trig_source='', enable=False, edge=Edge.names()[0], level=0.1): + assert edge in Edge.names() + self.trig_source = trig_source + self.enable = enable + self.edge = edge + self.level = level + + +class Channel: + def __init__(self, name='', source=DAQ_NIDAQ_source.names()[0]): + """ + Parameters + ---------- + + """ + self.name = name + assert source in DAQ_NIDAQ_source.names() + self.source = source + + +class AChannel(Channel): + def __init__(self, analog_type=DAQ_analog_types.names()[0], value_min=-10., value_max=+10., **kwargs): + """ + Parameters + ---------- + min: (float) minimum value for the configured input channel (could be voltage, amps, temperature...) + max: (float) maximum value for the configured input channel + """ + super().__init__(**kwargs) + self.value_min = value_min + self.value_max = value_max + self.analog_type = analog_type + + +class AIChannel(AChannel): + def __init__(self, termination=DAQ_termination.names()[0], **kwargs): + super().__init__(**kwargs) + assert termination in DAQ_termination.names() + self.termination = termination + + +class AIThermoChannel(AIChannel): + def __init__(self, thermo_type=DAQ_thermocouples.names()[0], **kwargs): + super().__init__(**kwargs) + assert thermo_type in DAQ_thermocouples.names() + self.thermo_type = thermo_type + + +class AOChannel(AChannel): + def __init__(self, **kwargs): + super().__init__(**kwargs) + + +class Counter(Channel): + def __init__(self, edge=Edge.names()[0], **kwargs): + assert edge in Edge.names() + super().__init__(**kwargs) + self.edge = edge + self.counter_type = "Edge Counter" + + +class ClockCounter(Counter): + def __init__(self, clock_frequency, **kwargs): + super().__init__(**kwargs) + self.clock_frequency = clock_frequency + self.counter_type = "Clock Output" + + +class SemiPeriodCounter(Counter): + def __init__(self, value_max, **kwargs): + super().__init__(**kwargs) + self.value_max = value_max + self.counter_type = "SemiPeriod Input" + + +class DigitalChannel(Channel): + def __init__(self, **kwargs): + super().__init__(**kwargs) + + +class DOChannel(DigitalChannel): + def __init__(self, **kwargs): + super().__init__(**kwargs) + + +class DIChannel(DigitalChannel): + def __init__(self, **kwargs): + super().__init__(**kwargs) + + +def try_string_buffer(fun, *args): + """ + generic function to read string from a PyDAQmx function making sure the chosen buffer is large enough + Parameters + ---------- + fun: (PyDAQmx function pointer) e.g. PyDAQmx.DAQmxGetSysDevNames + kwargs + + Returns + ------- + + """ + buff_size = 1024 + while True: + buff = PyDAQmx.create_string_buffer(buff_size) + try: + if not not len(args): + fun(args[0], buff, buff_size) + else: + fun(buff, buff_size) + break + + except Exception as e: + if isinstance(e, PyDAQmx.DAQmxFunctions.DAQException): + if e.error == -200228: # BufferTooSmallForStringError + buff_size = 2 * buff_size + else: + raise e + else: + raise e + return buff.value.decode() + + +class DAQmx: + """Wrapper around the PyDAQmx package giving an easy to use object to instantiate channels and tasks""" + def __init__(self): + self.devices = [] + self.channels = [] + self._device = None + self._task = None + self.update_NIDAQ_devices() + self.update_NIDAQ_channels() + self.c_callback = None + self.callback_data = None + self.is_scalar = True + self.write_buffer = np.array([0.]) + + @property + def task(self): + return self._task + + @property + def device(self): + return self._device + + @device.setter + def device(self, device): + if device not in self.devices: + raise IOError(f'your device: {device} is not known or connected') + self._device = device + + def update_NIDAQ_devices(self): + self.devices = self.get_NIDAQ_devices() + + @classmethod + def get_NIDAQ_devices(cls): + """Get list of NI connected devices + + Returns + ------- + list + list of devices as strings to be used in subsequent commands + """ + try: + string = try_string_buffer(PyDAQmx.DAQmxGetSysDevNames) + devices = string.split(', ') + if devices == ['']: + devices = [] + return devices + except: + return [] + + def update_NIDAQ_channels(self, source_type=None): + self.channels = self.get_NIDAQ_channels(self.devices, source_type=source_type) + + @classmethod + def get_NIDAQ_channels(cls, devices=None, source_type=None): + """Get the list of available channels for all NiDAq connected devices + + Parameters + ---------- + devices: list + list of strings, each one being a connected device + source_type: str + One of the entries of DAQ_NIDAQ_source enum + + Returns + ------- + List of str containing device and channel names + + """ + if devices is None: + devices = cls.get_NIDAQ_devices() + + if source_type is None: + source_type = DAQ_NIDAQ_source.names() + if not isinstance(source_type, list): + source_type = [source_type] + channels_tot = [] + if not not devices: + for device in devices: + for source in source_type: + if source == DAQ_NIDAQ_source['Analog_Input'].name: # analog input + string = try_string_buffer(PyDAQmx.DAQmxGetDevAIPhysicalChans, device) + elif source == DAQ_NIDAQ_source['Counter'].name: # counter + string = try_string_buffer(PyDAQmx.DAQmxGetDevCIPhysicalChans, device) + elif source == DAQ_NIDAQ_source['Analog_Output'].name: # analog output + string = try_string_buffer(PyDAQmx.DAQmxGetDevAOPhysicalChans, device) + elif source == DAQ_NIDAQ_source['Digital_Output'].name: # digital output + string = try_string_buffer(PyDAQmx.DAQmxGetDevDOLines, device) + elif source == DAQ_NIDAQ_source['Digital_Input'].name: # digital output + string = try_string_buffer(PyDAQmx.DAQmxGetDevDILines, device) + elif source == DAQ_NIDAQ_source['Terminals'].name: # digital output + string = try_string_buffer(PyDAQmx.DAQmxGetDevTerminals, device) + + channels = string.split(', ') + if channels != ['']: + channels_tot.extend(channels) + + return channels_tot + + @classmethod + def getAOMaxRate(cls, device): + data = PyDAQmx.c_double() + PyDAQmx.DAQmxGetDevAOMaxRate(device, PyDAQmx.byref(data)) + return data.value + + @classmethod + def getAIMaxRate(cls, device): + data = PyDAQmx.c_double() + PyDAQmx.DAQmxGetDevAIMaxSingleChanRate(device, PyDAQmx.byref(data)) + return data.value + + @classmethod + def isAnalogTriggeringSupported(cls, device): + data = PyDAQmx.c_uint32() + PyDAQmx.DAQmxGetDevAnlgTrigSupported(device, PyDAQmx.byref(data)) + return bool(data.value) + + @classmethod + def isDigitalTriggeringSupported(cls, device): + data = PyDAQmx.c_uint32() + PyDAQmx.DAQmxGetDevDigTrigSupported(device, PyDAQmx.byref(data)) + return bool(data.value) + + @classmethod + def getTriggeringSources(cls, devices=None): + sources = [] + if devices is None: + devices = cls.get_NIDAQ_devices() + + for device in devices: + if cls.isDigitalTriggeringSupported(device): + string = try_string_buffer(PyDAQmx.DAQmxGetDevTerminals, device) + channels = [chan for chan in string.split(', ') if 'PFI' in chan] + if channels != ['']: + sources.extend(channels) + if cls.isAnalogTriggeringSupported(device): + channels = cls.get_NIDAQ_channels(devices=[device], source_type='Analog_Input') + if channels != ['']: + sources.extend(channels) + return sources + + def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_settings=TriggerSettings()): + + try: + if self._task is not None: + if isinstance(self._task, PyDAQmx.Task): + self._task.ClearTask() + + self._task = None + self.c_callback = None + + self._task = PyDAQmx.Task() + + ## create all channels one task for one type of channels + for channel in channels: + if channel.source == 'Analog_Input': #analog input + if channel.analog_type == "Voltage": + err_code = self._task.CreateAIVoltageChan(channel.name, "analog voltage task", + DAQ_termination[channel.termination].value, + channel.value_min, + channel.value_max, + PyDAQmx.DAQmx_Val_Volts, None) + + elif channel.analog_type == "Current": + err_code = self._task.CreateAICurrentChan(channel.name, "", + DAQ_termination[channel.termination].value, + channel.value_min, + channel.value_max, + PyDAQmx.DAQmx_Val_Amps, PyDAQmx.DAQmx_Val_Internal, + 0., None) + + elif channel.analog_type == "Thermocouple": + err_code = self._task.CreateAIThrmcplChan(channel.name, "", + channel.value_min, + channel.value_max, + PyDAQmx.DAQmx_Val_DegC, + DAQ_termination[channel.thermo_type].value, + PyDAQmx.DAQmx_Val_BuiltIn, 0., "") + + elif channel.source == 'Counter': #counter + if channel.counter_type == "Edge Counter": + err_code = self._task.CreateCICountEdgesChan(channel.name, "", + Edge[channel.edge].value, 0, + PyDAQmx.DAQmx_Val_CountUp) + elif channel.counter_type == "Clock Output": + err_code = self._task.CreateCOPulseChanFreq(channel.name, "clock task", + # units, Hertz in our case + PyDAQmx.DAQmx_Val_Hz, + # idle state + PyDAQmx.DAQmx_Val_Low, + # initial delay + 0, + # pulse frequency + channel.clock_frequency, + # duty cycle of pulses, 0.5 such that + # high and low duration are both + # equal to count_interval + 0.5) + elif channel.counter_type == "SemiPeriod Input": + err_code = self._task.CreateCISemiPeriodChan(channel.name, "counter task", + 0, # expected min + channel.value_max, # expected max + PyDAQmx.DAQmx_Val_Ticks, "") + + + if not not err_code: + status = self.DAQmxGetErrorString(err_code) + raise IOError(status) + + elif channel.source == 'Analog_Output': # Analog_Output + if channel.analog_type == "Voltage": + err_code = self._task.CreateAOVoltageChan(channel.name, "", + channel.value_min, + channel.value_max, + PyDAQmx.DAQmx_Val_Volts, None) + + if channel.analog_type == "Current": + err_code = self._task.CreateAOCurrentChan(channel.name, "", + channel.value_min, + channel.value_max, + PyDAQmx.DAQmx_Val_Amps, None) + + elif channel.source == 'Digital_Output': #Digital_Output + err_code = self._task.CreateDOChan(channel.name, "", PyDAQmx.DAQmx_Val_ChanPerLine) + if not not err_code: + status = self.DAQmxGetErrorString(err_code) + raise IOError(status) + + elif channel.source == 'Digital_Input': #Digital_Input + err_code = self._task.CreateDIChan(channel.name, "", PyDAQmx.DAQmx_Val_ChanPerLine) + if not not err_code: + status = self.DAQmxGetErrorString(err_code) + raise IOError(status) + + ## configure the timing + if clock_settings.repetition: + mode = PyDAQmx.DAQmx_Val_ContSamps + else: + mode = PyDAQmx.DAQmx_Val_FiniteSamps + if clock_settings.Nsamples > 1 and err_code == 0: + if isinstance(clock_settings, ClockSettings): + err_code =\ + self._task.CfgSampClkTiming(clock_settings.source, clock_settings.frequency, + Edge[clock_settings.edge].value, + mode, + clock_settings.Nsamples) + elif isinstance(clock_settings, ChangeDetectionSettings): + err_code =\ + self._task.CfgChangeDetectionTiming(clock_settings.rising_channel, + clock_settings.falling_channel, + mode, + clock_settings.Nsamples) + + if not not err_code: + status = self.DAQmxGetErrorString(err_code) + raise IOError(status) + + # if channel.source == 'Analog_Input': # analog input + # if isinstance(clock_settings, ClockSettings): + # err_code =\ + # self._task.CfgSampClkTiming(clock_settings.source, clock_settings.frequency, + # Edge[clock_settings.edge].value, + # mode, + # clock_settings.Nsamples) + # elif isinstance(clock_settings, ChangeDetectionSettings): + # err_code =\ + # self._task.CfgChangeDetectionTiming(clock_settings.source, + # clock_settings.rising_channel, + # clock_settings.falling_channel, + # mode, + # clock_settings.Nsamples) + # + # if not not err_code: + # status = self.DAQmxGetErrorString(err_code) + # raise IOError(status) + # + # elif channel.source == 'Counter': # counter + # pass + # + # elif channel.source == 'Analog_Output': # Analog_Output + # if clock_settings.Nsamples > 1 and err_code == 0: + # + # if isinstance(clock_settings, ClockSettings): + # err_code = self._task.CfgSampClkTiming(clock_settings.source, + # clock_settings.frequency, + # Edge[clock_settings.edge].value, + # mode, + # clock_settings.Nsamples) + # elif isinstance(clock_settings, ChangeDetectionSettings): + # err_code = \ + # self._task.CfgChangeDetectionTiming(clock_settings.source, + # clock_settings.rising_channel, + # clock_settings.falling_channel, + # mode, + # clock_settings.Nsamples) + # + # if not not err_code: + # status = self.DAQmxGetErrorString(err_code) + # raise IOError(status) + # + # else: + # pass + + ##configure the triggering, except for counters + if not trigger_settings.enable: + if channel.source == 'Counter': + pass + else: + err = self._task.DisableStartTrig() + if err != 0: + raise IOError(self.DAQmxGetErrorString(err)) + else: + if 'PF' in trigger_settings.trig_source: + self._task.CfgDigEdgeStartTrig(trigger_settings.trig_source, + Edge[trigger_settings.edge].value) + elif 'ai' in trigger_settings.trig_source: + self._task.CfgAnlgEdgeStartTrig(trigger_settings.trig_source, + Edge[trigger_settings.edge].value, + PyDAQmx.c_double(trigger_settings.level)) + else: + raise IOError('Unsupported Trigger source') + + except Exception as e: + print(e) + + def register_callback(self, callback, event='done', nsamples=1): + + if event == 'done': + self.c_callback = PyDAQmx.DAQmxDoneEventCallbackPtr(callback) + self._task.RegisterDoneEvent(0, self.c_callback, None) + elif event == 'sample': + self.c_callback = PyDAQmx.DAQmxSignalEventCallbackPtr(callback) + self._task.RegisterSignalEvent(PyDAQmx.DAQmx_Val_SampleCompleteEvent, 0, self.c_callback, None) + elif event == 'Nsamples': + self.c_callback = PyDAQmx.DAQmxEveryNSamplesEventCallbackPtr(callback) + self._task.RegisterEveryNSamplesEvent(PyDAQmx.DAQmx_Val_Acquired_Into_Buffer, nsamples, + 0, self.c_callback, None) + + def get_last_write_index(self): + if self.task is not None: + index_buffer = PyDAQmx.c_uint64() + ret = self._task.GetWriteCurrWritePos(PyDAQmx.byref(index_buffer)) + if not not ret: + raise IOError(self.DAQmxGetErrorString(ret)) + else: + return index_buffer.value + else: + return -1 + + def get_last_write(self): + if self.is_scalar: + return self.write_buffer[-1] + else: + index = self.get_last_write_index() + if index != -1: + return self.write_buffer[index % len(self.write_buffer)] + + else: + return 0. + + def writeAnalog(self, Nsamples, Nchannels, values, autostart=False): + """ + Write Nsamples on N analog output channels + Parameters + ---------- + Nsamples: (int) numver of samples to write on each channel + Nchannels: (int) number of AO channels defined in the task + values: (ndarray) 2D array (or flattened array) of size Nsamples * Nchannels + + Returns + ------- + + """ + if np.prod(values.shape) != Nsamples * Nchannels: + raise ValueError(f'The shape of analog outputs values is incorrect, should be {Nsamples} x {Nchannels}') + + if len(values.shape) != 1: + values = values.reshape((Nchannels * Nsamples)) + self.write_buffer = values + + timeout = -1 + if values.size == 1: + self._task.WriteAnalogScalarF64(autostart, timeout, values[0], None) + self.is_scalar = True + + else: + self.is_scalar = False + read = PyDAQmx.int32() + self._task.WriteAnalogF64(Nsamples, autostart, timeout, PyDAQmx.DAQmx_Val_GroupByChannel, values, + PyDAQmx.byref(read), None) + if read.value != Nsamples: + raise IOError(f'Insufficient number of samples have been written:{read.value}/{Nsamples}') + + def readAnalog(self, Nchannels, clock_settings): + read = PyDAQmx.int32() + N = clock_settings.Nsamples + data = np.zeros(N * Nchannels, dtype=np.float64) + timeout = N * Nchannels * 1 / clock_settings.frequency * 2 # set to twice the time it should take to acquire the data + + self._task.ReadAnalogF64(N, timeout, PyDAQmx.DAQmx_Val_GroupByChannel, data, len(data), + PyDAQmx.byref(read), None) + if read.value == N: + return data + else: + raise IOError(f'Insufficient number of samples have been read:{read.value}/{N}') + + def readCounter(self, Nchannels, counting_time=10., read_function="Ex"): + + data_counter = np.zeros(Nchannels, dtype='uint32') + read = PyDAQmx.int32() + if read_function == "Ex": + self._task.ReadCounterU32Ex(PyDAQmx.DAQmx_Val_Auto, 2*counting_time, + PyDAQmx.DAQmx_Val_GroupByChannel, + data_counter, + Nchannels, PyDAQmx.byref(read), None) + else: + self._task.ReadCounterU32(PyDAQmx.DAQmx_Val_Auto, 2*counting_time, + data_counter, Nchannels, PyDAQmx.byref(read), None) + + self._task.StopTask() + + if read.value == Nchannels: + return data_counter + else: + raise IOError(f'Insufficient number of samples have been read:{read}/{Nchannels}') + + def readDigital(self, Nchannels): + read = PyDAQmx.int32() + bytes_sample = PyDAQmx.int32() + data = np.zeros(Nchannels, dtype='uint8') + self._task.ReadDigitalLines(Nchannels, 0, PyDAQmx.DAQmx_Val_GroupByChannel, + data, Nchannels, PyDAQmx.byref(read), PyDAQmx.byref(bytes_sample), None); + + if read.value == Nchannels: + return data + else: + raise IOError(f'Insufficient number of samples have been read:{read}/{Nchannels}') + + def writeDigital(self, Nchannels, values, autostart=False): + if np.prod(values.shape) != Nchannels: + raise ValueError(f'The shape of digital outputs values is incorrect, should be {Nchannels}') + values.astype(np.uint8) + written = PyDAQmx.int32() + self._task.WriteDigitalLines(Nchannels, autostart, 0, PyDAQmx.DAQmx_Val_GroupByChannel, + values, PyDAQmx.byref(written), None) + if written.value != Nchannels: + raise IOError(f'Insufficient number of samples have been written:{written}/{Nchannels}') + + @classmethod + def getAIVoltageRange(cls, device='Dev1'): + buff_size = 100 + ranges = ctypes.pointer((buff_size*ctypes.c_double)()) + ret = PyDAQmx.DAQmxGetDevAIVoltageRngs(device, ranges[0], buff_size) + if ret == 0: + return [tuple(ranges.contents[2*ind:2*(ind+1)]) for ind in range(int(buff_size/2-2)) + if np.abs(ranges.contents[2*ind]) > 1e-12] + return [(-10., 10.)] + + @classmethod + def getAOVoltageRange(cls, device='Dev1'): + buff_size = 100 + ranges = ctypes.pointer((buff_size*ctypes.c_double)()) + ret = PyDAQmx.DAQmxGetDevAOVoltageRngs(device, ranges[0], buff_size) + if ret == 0: + return [tuple(ranges.contents[2*ind:2*(ind+1)]) for ind in range(int(buff_size/2-2)) + if np.abs(ranges.contents[2*ind]) > 1e-12] + return [(-10., 10.)] + + def stop(self): + if self._task is not None: + self._task.StopTask() + + def start(self): + if self._task is not None: + self._task.StartTask() + + def close(self): + """ + close the current task. + """ + if self._task is not None: + self._task.StopTask() + self._task.ClearTask() + self._task = None + + @classmethod + def DAQmxGetErrorString(cls, error_code): + if error_code is None: + return '' + else: + buffer = PyDAQmx.create_string_buffer(1024) + PyDAQmx.DAQmxGetErrorString(error_code, buffer, len(buffer)) + return buffer.value.decode() + + def isTaskDone(self): + done = PyDAQmx.bool32(False) + self._task.GetTaskComplete(PyDAQmx.byref(done)) + return bool(done.value) + + def waitTaskDone(self, timeout=10.): + ret = self._task.WaitUntilTaskDone(timeout) + if ret != 0: + logger.info(self.DAQmxGetErrorString(ret)) + + def refresh_hardware(self): + """ + Refresh the NIDAQ hardware from settings values. + + See Also + -------- + update_NIDAQ_devices, update_NIDAQ_channels + """ + devices = self.update_NIDAQ_devices() + self.update_NIDAQ_channels(devices) + + +if __name__ == '__main__': + print(DAQmx.get_NIDAQ_channels()) + pass From b3b899139d810149b03a491d6e9430ba1f302086 Mon Sep 17 00:00:00 2001 From: jerlfan Date: Thu, 7 Dec 2023 17:31:28 +0100 Subject: [PATCH 04/57] adding the use of the nidaqmx module --- plugin_info.toml | 2 +- .../plugins_0D/daq_0Dviewer_DAQmx_counter.py | 2 +- .../hardware/national_instruments/_nidaqmx.py | 23 ++- .../national_instruments/counter_test.py | 19 -- .../{daqmxNI.py => daqmx-ni.py} | 170 +++++++++--------- 5 files changed, 105 insertions(+), 111 deletions(-) delete mode 100644 src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_test.py rename src/pymodaq_plugins_daqmx/hardware/national_instruments/{daqmxNI.py => daqmx-ni.py} (83%) diff --git a/plugin_info.toml b/plugin_info.toml index d5abec0..166e6c0 100644 --- a/plugin_info.toml +++ b/plugin_info.toml @@ -11,7 +11,7 @@ license = 'MIT' [plugin-install] #packages required for your plugin: -packages-required = ['pydaqmx', 'pymodaq>4.0.1'] +packages-required = ['pydaqmx','nidaqmx', 'pymodaq>4.0.1'] ## [features] # defines the plugin features contained into this plugin diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py index 22082c6..5236f83 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py @@ -3,7 +3,7 @@ from pymodaq.utils.data import DataWithAxes, DataToExport, DataSource from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters, main from pymodaq.utils.parameter import Parameter - +import nidaqmx from pymodaq_plugins_daqmx.hardware.national_instruments.daqmx import DAQmx, \ Edge, ClockSettings, Counter, ClockCounter, TriggerSettings diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/_nidaqmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/_nidaqmx.py index e1f1ec6..eac34f0 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/_nidaqmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/_nidaqmx.py @@ -2,19 +2,28 @@ from nidaqmx.constants import AcquisitionType,Edge import time import matplotlib.pyplot as plt +import time +import numpy as np task=nidaqmx.Task() -task.ci_channels.add_ci_count_edges_chan("Dev1/ctr0") +CI_channel = task.ci_channels.add_ci_count_edges_chan("Dev1/ctr0") + +print(task.ci_channels.channel_names) +nidaqmx.CtrTime(0.2,0.1) +a=0 +b=0 +data=[] task.start() -data=task.read(512) -#for i in range(512): -# mesure0 = task.read() -# data.append(task.read()-mesure0) -task.stop() +for i in range(512): + a=task.read() + data.append(a) + print(a) -print(data) + + +task.stop() task.close() plt.plot(data) plt.show() \ No newline at end of file diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_test.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_test.py deleted file mode 100644 index 11e474a..0000000 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_test.py +++ /dev/null @@ -1,19 +0,0 @@ -import PyDAQmx -import ctypes -import numpy as np - - -counter_input = PyDAQmx.Task() -read = PyDAQmx.int32() -data = np.zeros((1000,),dtype=np.float64) - -#DAQmx Configure Code -counter_input.CreateCICountEdgesChan("Dev1/ctr0","",PyDAQmx.DAQmx_Val_Rising,0,PyDAQmx.DAQmx_Val_CountUp) - -#DAQmx Start Code -counter_input.StartTask() - -#DAQmx Read Code -counter_input.ReadCounterScalarU32(10.0,PyDAQmx.byref(data),None) - -print(read.value) \ No newline at end of file diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxNI.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmx-ni.py similarity index 83% rename from src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxNI.py rename to src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmx-ni.py index 7ce5fe1..e20cc87 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxNI.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmx-ni.py @@ -1,4 +1,4 @@ -import PyDAQmx +import nidaqmx import ctypes from enum import IntEnum import numpy as np @@ -37,9 +37,9 @@ class DAQ_analog_types(IntEnum): **Attributes** **Type** =============== ========== """ - Voltage = PyDAQmx.DAQmx_Val_Voltage - Current = PyDAQmx.DAQmx_Val_Current - Thermocouple = PyDAQmx.DAQmx_Val_Temp_TC + Voltage = nidaqmx.constants.UsageTypeAI.VOLTAGE.value + Current = nidaqmx.constants.UsageTypeAI.CURRENT.value + Thermocouple = nidaqmx.constants.UsageTypeAI.TEMPERATURE_THERMOCOUPLE.value @classmethod def names(cls): @@ -58,14 +58,14 @@ class DAQ_thermocouples(IntEnum): **Attributes** **Type** =============== ========== """ - J = PyDAQmx.DAQmx_Val_J_Type_TC - K = PyDAQmx.DAQmx_Val_K_Type_TC - N = PyDAQmx.DAQmx_Val_N_Type_TC - R = PyDAQmx.DAQmx_Val_R_Type_TC - S = PyDAQmx.DAQmx_Val_S_Type_TC - T = PyDAQmx.DAQmx_Val_T_Type_TC - B = PyDAQmx.DAQmx_Val_B_Type_TC - E = PyDAQmx.DAQmx_Val_E_Type_TC + J = nidaqmx.constants.ThermocoupleType.J.value + K = nidaqmx.constants.ThermocoupleType.K.value + N = nidaqmx.constants.ThermocoupleType.N.value + R = nidaqmx.constants.ThermocoupleType.R.value + S = nidaqmx.constants.ThermocoupleType.S.value + T = nidaqmx.constants.ThermocoupleType.T.value + B = nidaqmx.constants.ThermocoupleType.B.value + E = nidaqmx.constants.ThermocoupleType.E.value @classmethod def names(cls): @@ -80,11 +80,11 @@ class DAQ_termination(IntEnum): **Attributes** **Type** =============== ========== """ - Auto = PyDAQmx.DAQmx_Val_Cfg_Default - RSE = PyDAQmx.DAQmx_Val_RSE - NRSE = PyDAQmx.DAQmx_Val_NRSE - Diff = PyDAQmx.DAQmx_Val_Diff - Pseudodiff = PyDAQmx.DAQmx_Val_PseudoDiff + Auto = nidaqmx.constants.TerminalConfiguration.DEFAULT.value + RSE = nidaqmx.constants.TerminalConfiguration.RSE.value + NRSE = nidaqmx.constants.TerminalConfiguration.NRSE.value + Diff = nidaqmx.constants.TerminalConfiguration.DIFF.value + Pseudodiff = nidaqmx.constants.TerminalConfiguration.PSEUDO_DIFF.value @classmethod def names(cls): @@ -94,8 +94,8 @@ def names(cls): class Edge(IntEnum): """ """ - Rising = PyDAQmx.DAQmx_Val_Rising - Falling = PyDAQmx.DAQmx_Val_Falling + Rising = nidaqmx.constants.Edge.RISING.value + Falling = nidaqmx.constants.Edge.FALLING.value @classmethod def names(cls): @@ -105,8 +105,10 @@ def names(cls): class ClockMode(IntEnum): """ """ - Finite = PyDAQmx.DAQmx_Val_Rising - Continuous = PyDAQmx.DAQmx_Val_Falling + #Finite = PyDAQmx.DAQmx_Val_Rising // These values were not correct? + #Continuous = PyDAQmx.DAQmx_Val_Falling + Finite = nidaqmx.constants.AcquisitionType.FINITE.value + Continuous = nidaqmx.constants.AcquisitionType.CONTINUOUS.value @classmethod def names(cls): @@ -302,8 +304,8 @@ def get_NIDAQ_devices(cls): list of devices as strings to be used in subsequent commands """ try: - string = try_string_buffer(PyDAQmx.DAQmxGetSysDevNames) - devices = string.split(', ') + #string = try_string_buffer(PyDAQmx.DAQmxGetSysDevNames) + devices = nidaqmx.system.System.local().devices.device_names#string.split(', ') if devices == ['']: devices = [] return devices @@ -341,19 +343,19 @@ def get_NIDAQ_channels(cls, devices=None, source_type=None): for device in devices: for source in source_type: if source == DAQ_NIDAQ_source['Analog_Input'].name: # analog input - string = try_string_buffer(PyDAQmx.DAQmxGetDevAIPhysicalChans, device) + string = nidaqmx.system.System.local().devices[device].ai_physical_chans.channel_names elif source == DAQ_NIDAQ_source['Counter'].name: # counter - string = try_string_buffer(PyDAQmx.DAQmxGetDevCIPhysicalChans, device) + string = nidaqmx.system.System.local().devices[device].ci_physical_chans.channel_names elif source == DAQ_NIDAQ_source['Analog_Output'].name: # analog output - string = try_string_buffer(PyDAQmx.DAQmxGetDevAOPhysicalChans, device) + string = nidaqmx.system.System.local().devices[device].ao_physical_chans.channel_names elif source == DAQ_NIDAQ_source['Digital_Output'].name: # digital output - string = try_string_buffer(PyDAQmx.DAQmxGetDevDOLines, device) - elif source == DAQ_NIDAQ_source['Digital_Input'].name: # digital output - string = try_string_buffer(PyDAQmx.DAQmxGetDevDILines, device) - elif source == DAQ_NIDAQ_source['Terminals'].name: # digital output - string = try_string_buffer(PyDAQmx.DAQmxGetDevTerminals, device) + string = nidaqmx.system.System.local().devices[device].do_lines.channel_names + elif source == DAQ_NIDAQ_source['Digital_Input'].name: # digital iutput + string = nidaqmx.system.System.local().devices[device].di_lines.channel_names + elif source == DAQ_NIDAQ_source['Terminals'].name: # terminals + string = nidaqmx.system.System.local().devices[device].terminals - channels = string.split(', ') + channels = string if channels != ['']: channels_tot.extend(channels) @@ -361,27 +363,27 @@ def get_NIDAQ_channels(cls, devices=None, source_type=None): @classmethod def getAOMaxRate(cls, device): - data = PyDAQmx.c_double() - PyDAQmx.DAQmxGetDevAOMaxRate(device, PyDAQmx.byref(data)) - return data.value + #data = PyDAQmx.c_double() + #PyDAQmx.DAQmxGetDevAOMaxRate(device, PyDAQmx.byref(data)) + return nidaqmx.system.device.Device(device).ao_max_rate @classmethod def getAIMaxRate(cls, device): - data = PyDAQmx.c_double() - PyDAQmx.DAQmxGetDevAIMaxSingleChanRate(device, PyDAQmx.byref(data)) - return data.value + #data = PyDAQmx.c_double() + #PyDAQmx.DAQmxGetDevAIMaxSingleChanRate(device, PyDAQmx.byref(data)) + return nidaqmx.system.device.Device(device).ai_max_single_chan_rate @classmethod def isAnalogTriggeringSupported(cls, device): - data = PyDAQmx.c_uint32() - PyDAQmx.DAQmxGetDevAnlgTrigSupported(device, PyDAQmx.byref(data)) - return bool(data.value) + #data = PyDAQmx.c_uint32() + #PyDAQmx.DAQmxGetDevAnlgTrigSupported(device, PyDAQmx.byref(data)) + return nidaqmx.system.device.Device(device).anlg_trig_supported @classmethod def isDigitalTriggeringSupported(cls, device): - data = PyDAQmx.c_uint32() - PyDAQmx.DAQmxGetDevDigTrigSupported(device, PyDAQmx.byref(data)) - return bool(data.value) + #data = PyDAQmx.c_uint32() + #PyDAQmx.DAQmxGetDevDigTrigSupported(device, PyDAQmx.byref(data)) + return nidaqmx.system.device.Device(device).dig_trig_supported @classmethod def getTriggeringSources(cls, devices=None): @@ -391,12 +393,12 @@ def getTriggeringSources(cls, devices=None): for device in devices: if cls.isDigitalTriggeringSupported(device): - string = try_string_buffer(PyDAQmx.DAQmxGetDevTerminals, device) - channels = [chan for chan in string.split(', ') if 'PFI' in chan] + string = nidaqmx.system.device.Device(device).terminals + channels = [chan for chan in string if 'PFI' in chan] if channels != ['']: sources.extend(channels) if cls.isAnalogTriggeringSupported(device): - channels = cls.get_NIDAQ_channels(devices=[device], source_type='Analog_Input') + channels = nidaqmx.system.device.Device(device).ai_physical_chans.channel_names if channels != ['']: sources.extend(channels) return sources @@ -405,51 +407,52 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti try: if self._task is not None: - if isinstance(self._task, PyDAQmx.Task): + if isinstance(self._task, nidaqmx.Task): self._task.ClearTask() self._task = None self.c_callback = None - self._task = PyDAQmx.Task() + self._task = nidaqmx.Task() ## create all channels one task for one type of channels for channel in channels: if channel.source == 'Analog_Input': #analog input if channel.analog_type == "Voltage": - err_code = self._task.CreateAIVoltageChan(channel.name, "analog voltage task", + err_code = self._task.ai_channels.add_ai_voltage_chan(channel.name, "", DAQ_termination[channel.termination].value, channel.value_min, channel.value_max, - PyDAQmx.DAQmx_Val_Volts, None) + nidaqmx.constants.VoltageUnits.VOLTS, None) elif channel.analog_type == "Current": - err_code = self._task.CreateAICurrentChan(channel.name, "", + err_code = self._task.ai_channels.add_ai_current_chan(channel.name, "", DAQ_termination[channel.termination].value, channel.value_min, channel.value_max, - PyDAQmx.DAQmx_Val_Amps, PyDAQmx.DAQmx_Val_Internal, + nidaqmx.constants.CurrentUnits.AMPS, + nidaqmx.constants.CurrentShuntResistorLocation.INTERNAL, 0., None) elif channel.analog_type == "Thermocouple": - err_code = self._task.CreateAIThrmcplChan(channel.name, "", + err_code = self._task.ai_channels.add_ai_thrmcpl_chan(channel.name, "", channel.value_min, channel.value_max, - PyDAQmx.DAQmx_Val_DegC, + nidaqmx.constants.TemperatureUnits.DEG_C, DAQ_termination[channel.thermo_type].value, - PyDAQmx.DAQmx_Val_BuiltIn, 0., "") + nidaqmx.constants.CJCSource.BUILT_IN, 0., "") elif channel.source == 'Counter': #counter if channel.counter_type == "Edge Counter": - err_code = self._task.CreateCICountEdgesChan(channel.name, "", + err_code = self._task.ci_channels.add_ci_count_edges_chan(channel.name, "", Edge[channel.edge].value, 0, - PyDAQmx.DAQmx_Val_CountUp) + nidaqmx.constants.CountDirection.COUNT_UP) elif channel.counter_type == "Clock Output": - err_code = self._task.CreateCOPulseChanFreq(channel.name, "clock task", + err_code = self._task.co_channels.add_co_pulse_chan_freq(channel.name, "clock task", # units, Hertz in our case - PyDAQmx.DAQmx_Val_Hz, + nidaqmx.constants.FrequencyUnits.HZ, # idle state - PyDAQmx.DAQmx_Val_Low, + nidaqmx.constants.Level.LOW, # initial delay 0, # pulse frequency @@ -459,10 +462,10 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti # equal to count_interval 0.5) elif channel.counter_type == "SemiPeriod Input": - err_code = self._task.CreateCISemiPeriodChan(channel.name, "counter task", + err_code = self._task.ci_channels.add_ci_semi_period_chan(channel.name, "counter task", 0, # expected min channel.value_max, # expected max - PyDAQmx.DAQmx_Val_Ticks, "") + nidaqmx.constants.TimeUnits.TICKS, "") if not not err_code: @@ -720,29 +723,30 @@ def getAIVoltageRange(cls, device='Dev1'): @classmethod def getAOVoltageRange(cls, device='Dev1'): - buff_size = 100 - ranges = ctypes.pointer((buff_size*ctypes.c_double)()) - ret = PyDAQmx.DAQmxGetDevAOVoltageRngs(device, ranges[0], buff_size) - if ret == 0: - return [tuple(ranges.contents[2*ind:2*(ind+1)]) for ind in range(int(buff_size/2-2)) - if np.abs(ranges.contents[2*ind]) > 1e-12] - return [(-10., 10.)] + #buff_size = 100 + #ranges = ctypes.pointer((buff_size*ctypes.c_double)()) + #ret = PyDAQmx.DAQmxGetDevAOVoltageRngs(device, ranges[0], buff_size) + ret = nidaqmx.system.System.local().devices[device].ao_voltage_rngs + #if ret == 0: + # return [tuple(ranges.contents[2*ind:2*(ind+1)]) for ind in range(int(buff_size/2-2)) + # if np.abs(ranges.contents[2*ind]) > 1e-12] + return #[(-10., 10.)] Why this format is needed def stop(self): if self._task is not None: - self._task.StopTask() + self._task.stop() def start(self): if self._task is not None: - self._task.StartTask() + self._task.start() def close(self): """ close the current task. """ if self._task is not None: - self._task.StopTask() - self._task.ClearTask() + self._task.stop() + self._task.close() self._task = None @classmethod @@ -750,17 +754,17 @@ def DAQmxGetErrorString(cls, error_code): if error_code is None: return '' else: - buffer = PyDAQmx.create_string_buffer(1024) - PyDAQmx.DAQmxGetErrorString(error_code, buffer, len(buffer)) - return buffer.value.decode() - + #buffer = PyDAQmx.create_string_buffer(1024) + #PyDAQmx.DAQmxGetErrorString(error_code, buffer, len(buffer)) + #return buffer.value.decode() + return nidaqmx.errors.check_for_error(error_code) def isTaskDone(self): - done = PyDAQmx.bool32(False) - self._task.GetTaskComplete(PyDAQmx.byref(done)) - return bool(done.value) - + #done = PyDAQmx.bool32(False) + #self._task.GetTaskComplete(PyDAQmx.byref(done)) + #return bool(done.value) + return task.is_task_done() def waitTaskDone(self, timeout=10.): - ret = self._task.WaitUntilTaskDone(timeout) + ret = self._task.wait_until_done(timeout) if ret != 0: logger.info(self.DAQmxGetErrorString(ret)) From a34f000d813b83663eeb57a641d7f043baeecbbc Mon Sep 17 00:00:00 2001 From: jerlfan Date: Fri, 8 Dec 2023 09:20:47 +0100 Subject: [PATCH 05/57] replacing PyDAQmx by nidaqmx in daqmx-ni --- .../hardware/national_instruments/daqmx-ni.py | 135 ++++-------------- 1 file changed, 31 insertions(+), 104 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmx-ni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmx-ni.py index e20cc87..ea8e5d5 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmx-ni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmx-ni.py @@ -230,39 +230,6 @@ def __init__(self, **kwargs): super().__init__(**kwargs) -def try_string_buffer(fun, *args): - """ - generic function to read string from a PyDAQmx function making sure the chosen buffer is large enough - Parameters - ---------- - fun: (PyDAQmx function pointer) e.g. PyDAQmx.DAQmxGetSysDevNames - kwargs - - Returns - ------- - - """ - buff_size = 1024 - while True: - buff = PyDAQmx.create_string_buffer(buff_size) - try: - if not not len(args): - fun(args[0], buff, buff_size) - else: - fun(buff, buff_size) - break - - except Exception as e: - if isinstance(e, PyDAQmx.DAQmxFunctions.DAQException): - if e.error == -200228: # BufferTooSmallForStringError - buff_size = 2 * buff_size - else: - raise e - else: - raise e - return buff.value.decode() - - class DAQmx: """Wrapper around the PyDAQmx package giving an easy to use object to instantiate channels and tasks""" def __init__(self): @@ -474,44 +441,46 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti elif channel.source == 'Analog_Output': # Analog_Output if channel.analog_type == "Voltage": - err_code = self._task.CreateAOVoltageChan(channel.name, "", + err_code = self._task.ao_channels.add_ao_voltage_chan(channel.name, "", channel.value_min, channel.value_max, - PyDAQmx.DAQmx_Val_Volts, None) + nidaqmx.constants.VoltageUnits.VOLTS, None) if channel.analog_type == "Current": - err_code = self._task.CreateAOCurrentChan(channel.name, "", + err_code = self._task.ao_channels.add_ao_current_chan(channel.name, "", channel.value_min, channel.value_max, - PyDAQmx.DAQmx_Val_Amps, None) + nidaqmx.constants.VoltageUnits.VOLTS, None) elif channel.source == 'Digital_Output': #Digital_Output - err_code = self._task.CreateDOChan(channel.name, "", PyDAQmx.DAQmx_Val_ChanPerLine) + err_code = self._task.do_channels.add_do_chan(channel.name, "", + nidaqmx.constants.LineGrouping.CHAN_PER_LINE) if not not err_code: status = self.DAQmxGetErrorString(err_code) raise IOError(status) elif channel.source == 'Digital_Input': #Digital_Input - err_code = self._task.CreateDIChan(channel.name, "", PyDAQmx.DAQmx_Val_ChanPerLine) + err_code = self._task.di_channels.add_di_chan(channel.name, "", + nidaqmx.constants.LineGrouping.CHAN_PER_LINE) if not not err_code: status = self.DAQmxGetErrorString(err_code) raise IOError(status) ## configure the timing if clock_settings.repetition: - mode = PyDAQmx.DAQmx_Val_ContSamps + mode = nidaqmx.constants.AcquisitionType.CONTINUOUS else: - mode = PyDAQmx.DAQmx_Val_FiniteSamps + mode = nidaqmx.constants.AcquisitionType.FINITE if clock_settings.Nsamples > 1 and err_code == 0: if isinstance(clock_settings, ClockSettings): err_code =\ - self._task.CfgSampClkTiming(clock_settings.source, clock_settings.frequency, + self._task.timing.cfg_samp_clk_timing(clock_settings.source, clock_settings.frequency, Edge[clock_settings.edge].value, mode, clock_settings.Nsamples) elif isinstance(clock_settings, ChangeDetectionSettings): err_code =\ - self._task.CfgChangeDetectionTiming(clock_settings.rising_channel, + self._task.timing.cfg_change_detection_timing(clock_settings.rising_channel, clock_settings.falling_channel, mode, clock_settings.Nsamples) @@ -520,53 +489,6 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti status = self.DAQmxGetErrorString(err_code) raise IOError(status) - # if channel.source == 'Analog_Input': # analog input - # if isinstance(clock_settings, ClockSettings): - # err_code =\ - # self._task.CfgSampClkTiming(clock_settings.source, clock_settings.frequency, - # Edge[clock_settings.edge].value, - # mode, - # clock_settings.Nsamples) - # elif isinstance(clock_settings, ChangeDetectionSettings): - # err_code =\ - # self._task.CfgChangeDetectionTiming(clock_settings.source, - # clock_settings.rising_channel, - # clock_settings.falling_channel, - # mode, - # clock_settings.Nsamples) - # - # if not not err_code: - # status = self.DAQmxGetErrorString(err_code) - # raise IOError(status) - # - # elif channel.source == 'Counter': # counter - # pass - # - # elif channel.source == 'Analog_Output': # Analog_Output - # if clock_settings.Nsamples > 1 and err_code == 0: - # - # if isinstance(clock_settings, ClockSettings): - # err_code = self._task.CfgSampClkTiming(clock_settings.source, - # clock_settings.frequency, - # Edge[clock_settings.edge].value, - # mode, - # clock_settings.Nsamples) - # elif isinstance(clock_settings, ChangeDetectionSettings): - # err_code = \ - # self._task.CfgChangeDetectionTiming(clock_settings.source, - # clock_settings.rising_channel, - # clock_settings.falling_channel, - # mode, - # clock_settings.Nsamples) - # - # if not not err_code: - # status = self.DAQmxGetErrorString(err_code) - # raise IOError(status) - # - # else: - # pass - - ##configure the triggering, except for counters if not trigger_settings.enable: if channel.source == 'Counter': pass @@ -576,12 +498,12 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti raise IOError(self.DAQmxGetErrorString(err)) else: if 'PF' in trigger_settings.trig_source: - self._task.CfgDigEdgeStartTrig(trigger_settings.trig_source, + self._task.triggers.start_trigger.disable_start_trig(trigger_settings.trig_source, Edge[trigger_settings.edge].value) elif 'ai' in trigger_settings.trig_source: - self._task.CfgAnlgEdgeStartTrig(trigger_settings.trig_source, + self._task.triggers.start_trigger.cfg_anlg_edge_start_trig(trigger_settings.trig_source, Edge[trigger_settings.edge].value, - PyDAQmx.c_double(trigger_settings.level)) + trigger_settings.level) else: raise IOError('Unsupported Trigger source') @@ -591,15 +513,19 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti def register_callback(self, callback, event='done', nsamples=1): if event == 'done': - self.c_callback = PyDAQmx.DAQmxDoneEventCallbackPtr(callback) - self._task.RegisterDoneEvent(0, self.c_callback, None) + #self.c_callback = PyDAQmx.DAQmxDoneEventCallbackPtr(callback) + #self._task.register_done_event(0, self.c_callback, None) + self._task.register_done_event(nidaqmx.constants.Signal.SAMPLE_COMPLETE, callback) + #NOT SURE HERE elif event == 'sample': - self.c_callback = PyDAQmx.DAQmxSignalEventCallbackPtr(callback) - self._task.RegisterSignalEvent(PyDAQmx.DAQmx_Val_SampleCompleteEvent, 0, self.c_callback, None) + #self.c_callback = PyDAQmx.DAQmxSignalEventCallbackPtr(callback) + #self._task.register_signal_event(PyDAQmx.DAQmx_Val_SampleCompleteEvent, 0, self.c_callback, None) + self._task.register_every_n_samples_acquired_into_buffer_event(1, + callback) elif event == 'Nsamples': - self.c_callback = PyDAQmx.DAQmxEveryNSamplesEventCallbackPtr(callback) - self._task.RegisterEveryNSamplesEvent(PyDAQmx.DAQmx_Val_Acquired_Into_Buffer, nsamples, - 0, self.c_callback, None) + #self.c_callback = PyDAQmx.DAQmxEveryNSamplesEventCallbackPtr(callback) + self._task.register_every_n_samples_acquired_into_buffer_event(nsamples, + callback) def get_last_write_index(self): if self.task is not None: @@ -713,9 +639,10 @@ def writeDigital(self, Nchannels, values, autostart=False): @classmethod def getAIVoltageRange(cls, device='Dev1'): - buff_size = 100 - ranges = ctypes.pointer((buff_size*ctypes.c_double)()) - ret = PyDAQmx.DAQmxGetDevAIVoltageRngs(device, ranges[0], buff_size) + #buff_size = 100 + #ranges = ctypes.pointer((buff_size*ctypes.c_double)()) + #ret = PyDAQmx.DAQmxGetDevAIVoltageRngs(device, ranges[0], buff_size) + ret = nidaqmx.system.System.local().devices[device].ai_voltage_rngs if ret == 0: return [tuple(ranges.contents[2*ind:2*(ind+1)]) for ind in range(int(buff_size/2-2)) if np.abs(ranges.contents[2*ind]) > 1e-12] @@ -730,7 +657,7 @@ def getAOVoltageRange(cls, device='Dev1'): #if ret == 0: # return [tuple(ranges.contents[2*ind:2*(ind+1)]) for ind in range(int(buff_size/2-2)) # if np.abs(ranges.contents[2*ind]) > 1e-12] - return #[(-10., 10.)] Why this format is needed + return [tuple(ret)] #[(-10., 10.)] Why this format is needed def stop(self): if self._task is not None: From feb7235fa2dc1d45ebbc598328b6d2b3d5662978 Mon Sep 17 00:00:00 2001 From: jerlfan Date: Thu, 14 Dec 2023 23:09:23 +0100 Subject: [PATCH 06/57] working version of wrapper daqmxni Wrapper daqmxni as been implemented from nidaqmx library. Simple counter works. Other channels must be tested. Must add a "timer" to the counter. --- plugin_info.toml | 2 +- .../daq_0Dviewer_DAQmx_PLcounter.py | 4 - .../plugins_0D/daq_0Dviewer_DAQmx_counter.py | 91 +-- .../hardware/national_instruments/_nidaqmx.py | 29 - .../counter_clock_nidaqmx.py | 42 ++ .../counter_simple_nidaqmx.py | 41 + .../hardware/national_instruments/daqmx-ni.py | 712 ------------------ .../hardware/national_instruments/daqmx.py | 5 +- .../hardware/national_instruments/daqmxni.py | 592 +++++++++++++++ 9 files changed, 718 insertions(+), 800 deletions(-) delete mode 100644 src/pymodaq_plugins_daqmx/hardware/national_instruments/_nidaqmx.py create mode 100644 src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_clock_nidaqmx.py create mode 100644 src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_simple_nidaqmx.py delete mode 100644 src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmx-ni.py create mode 100644 src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py diff --git a/plugin_info.toml b/plugin_info.toml index 166e6c0..946fe9e 100644 --- a/plugin_info.toml +++ b/plugin_info.toml @@ -11,7 +11,7 @@ license = 'MIT' [plugin-install] #packages required for your plugin: -packages-required = ['pydaqmx','nidaqmx', 'pymodaq>4.0.1'] +packages-required = ['pydaqmx','nidaqmx','pymodaq>4.0.1'] ## [features] # defines the plugin features contained into this plugin diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_PLcounter.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_PLcounter.py index b1afc2b..154df24 100755 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_PLcounter.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_PLcounter.py @@ -8,10 +8,6 @@ Edge, ClockSettings, Counter, ClockCounter, TriggerSettings from PyDAQmx import DAQmx_Val_ContSamps -# DAQmx_Val_DoNotInvertPolarity, DAQmxConnectTerms, -# DAQmx_Val_FiniteSamps, DAQmx_Val_CurrReadPos, \ -# DAQmx_Val_DoNotOverwriteUnreadSamps - class DAQ_0DViewer_DAQmx_PLcounter(DAQ_Viewer_base): """ diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py index 5236f83..b66aa6a 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py @@ -4,11 +4,8 @@ from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters, main from pymodaq.utils.parameter import Parameter import nidaqmx -from pymodaq_plugins_daqmx.hardware.national_instruments.daqmx import DAQmx, \ - Edge, ClockSettings, Counter, ClockCounter, TriggerSettings - -from PyDAQmx import DAQmx_Val_ContSamps +from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import DAQmx, Edge, Counter class DAQ_0DViewer_DAQmx_counter(DAQ_Viewer_base): """ Plugin for a 0D PL counter, based on a NI card. @@ -18,10 +15,6 @@ class DAQ_0DViewer_DAQmx_counter(DAQ_Viewer_base): "type": "list", "limits": DAQmx.get_NIDAQ_channels(source_type="Counter")}, {"title": "Acq. Time (s):", "name": "acq_time", "type": "float", "value": 1., "default": 1.}, - {'title': 'Clock channel:', 'name': 'clock_channel', 'type': 'list', - 'limits': DAQmx.get_NIDAQ_channels(source_type='Counter')}, - {"title": "Clk Trig:", "name": "trig", - "type": "list", "limits": DAQmx.getTriggeringSources()} ] def ini_attributes(self): @@ -58,7 +51,7 @@ def ini_detector(self, controller=None): initialized: bool False if initialization failed otherwise True """ - self.controller = {"clock": DAQmx(), "counter": DAQmx()} + self.controller = DAQmx() #{"clock": DAQmx(), "counter": DAQmx()} try: self.update_tasks() initialized = True @@ -68,20 +61,15 @@ def ini_detector(self, controller=None): print(e) initialized = False info = "Error" - - #self.dte_signal_temp.emit(DataToExport(name='Counts', - # data=[DataWithAxes(name='Counts', data=[np.array([0])], - # source=DataSource['raw'], - # dim='Data0D', labels=['counts (Hz)'])])) - + return info, initialized - + def close(self): """Terminate the communication protocol""" pass #self.controller["clock"].close() #self.controller["counter"].close() - + def grab_data(self, Naverage=1, **kwargs): """Start a grab from the detector @@ -92,33 +80,34 @@ def grab_data(self, Naverage=1, **kwargs): kwargs: dict others optionals arguments """ - update = False # to decide if we do the initial set up or not - - - if 'live' in kwargs: - if kwargs['live'] != self.live: - update = True - self.live = kwargs['live'] - """ - if 'live' in kwargs: - if kwargs['live'] == self.live: - update = False # we are already live - self.live = kwargs['live'] - """ - - if update: - self.update_tasks() - self.controller["clock"].start() - - read_data = self.controller["counter"].readCounter(1, counting_time=self.counting_time) - data = read_data/self.counting_time # convert to cts/s + #update = False # to decide if we do the initial set up or not + + + #if 'live' in kwargs: + # if kwargs['live'] != self.live: + # update = True + # self.live = kwargs['live'] + # """ + #if 'live' in kwargs: + # if kwargs['live'] == self.live: + # update = False # we are already live + # self.live = kwargs['live'] + # """ + + #if update: + # self.update_tasks() + # self.controller["clock"].start() + + read_data = 32#self.controller.readCounter()#, counting_time=self.counting_time) + data = read_data #/self.counting_time # convert to cts/s self.dte_signal.emit(DataToExport(name='Counts', - data=[DataWithAxes(name='Counts', data=[data], + data=[DataWithAxes(name='Counts', data=[np.array([data])], source=DataSource['raw'], dim='Data0D', labels=['Counts (Hz)'])])) def stop(self): """Stop the current grab hardware wise if necessary""" + self.task.stop() self.close() self.emit_status(ThreadCommand('Update_Status', ['Acquisition stopped.'])) return '' @@ -126,24 +115,22 @@ def stop(self): def update_tasks(self): """Set up the counting tasks in the NI card.""" # Create channels - self.clock_channel = ClockCounter(1/self.settings.child("acq_time").value(), - name=self.settings.child("trig").value(), - source="Counter") + self.counter_channel = Counter(name=self.settings.child("counter_channel").value(), source="Counter", edge=Edge.names()[0]) - - self.controller["clock"].update_task(channels=[self.clock_channel], - clock_settings=ClockSettings(), - trigger_settings=TriggerSettings()) - self.controller["clock"].task.CfgImplicitTiming(DAQmx_Val_ContSamps, 1) - - self.controller["counter"].update_task(channels=[self.counter_channel], - clock_settings=ClockSettings(), - trigger_settings=TriggerSettings()) + # + #self.controller["clock"].update_task(channels=[self.clock_channel], + # clock_settings=ClockSettings(), + # trigger_settings=TriggerSettings()) + #self.controller["clock"].task.CfgImplicitTiming(DAQmx_Val_ContSamps, 1) + # + #self.controller["counter"].update_task(channels=[self.counter_channel], + # clock_settings=ClockSettings(), + # trigger_settings=TriggerSettings()) # connect the clock to the counter - self.controller["counter"].task.SetSampClkSrc("/" + self.clock_channel.name + "InternalOutput") + #self.controller["counter"].task.SetSampClkSrc("/" + self.clock_channel.name + "InternalOutput") + - if __name__ == '__main__': main(__file__) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/_nidaqmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/_nidaqmx.py deleted file mode 100644 index eac34f0..0000000 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/_nidaqmx.py +++ /dev/null @@ -1,29 +0,0 @@ -import nidaqmx -from nidaqmx.constants import AcquisitionType,Edge -import time -import matplotlib.pyplot as plt -import time -import numpy as np - -task=nidaqmx.Task() -CI_channel = task.ci_channels.add_ci_count_edges_chan("Dev1/ctr0") - - -print(task.ci_channels.channel_names) -nidaqmx.CtrTime(0.2,0.1) - -a=0 -b=0 -data=[] -task.start() -for i in range(512): - a=task.read() - data.append(a) - print(a) - - - -task.stop() -task.close() -plt.plot(data) -plt.show() \ No newline at end of file diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_clock_nidaqmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_clock_nidaqmx.py new file mode 100644 index 0000000..2f5577f --- /dev/null +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_clock_nidaqmx.py @@ -0,0 +1,42 @@ +import nidaqmx +import numpy as np +from nidaqmx.constants import Edge, AcquisitionType, FillMode, LineGrouping, CountDirection +from nidaqmx.stream_readers import CounterReader +from ctypes import c_uint32 + +def main(): + error = 0 + task_handle = nidaqmx.Task() + data = np.zeros(0, dtype=np.uint32) + read = np.uint32() + + try: + # DAQmx Configure Code + task_handle.ci_channels.add_ci_count_edges_chan("Dev1/ctr0", edge=Edge.RISING, initial_count=0, count_direction=CountDirection.COUNT_UP) + task_handle.timing.cfg_samp_clk_timing(source="/Dev1/Ctr1InternalOutput", rate=1000.0, active_edge=Edge.RISING, sample_mode=AcquisitionType.CONTINUOUS, samps_per_chan=1000) + + counter_reader=CounterReader(task_handle.in_stream) + # DAQmx Start Code + task_handle.start() + + print("En cours de lecture en continu. Appuyez sur Ctrl+C pour interrompre\n") + while True: + # DAQmx Read Code + #data=task_handle.read(number_of_samples_per_channel=1000, timeout=10.0) + #task_handle.in_stream.read_all_avail_samp + read=counter_reader.read_many_sample_uint32(data) + print("\rAcquis {} échantillons".format(read), end='', flush=True) + + except Exception as e: + print("\n") + if task_handle.is_task_done() == 0: + task_handle.stop() + task_handle.close() + print("Erreur DAQmx : {}".format(str(e))) + + finally: + print("\nFin du programme. Appuyez sur la touche Entrée pour quitter\n") + input() + +if __name__ == "__main__": + main() diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_simple_nidaqmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_simple_nidaqmx.py new file mode 100644 index 0000000..d6ceedc --- /dev/null +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_simple_nidaqmx.py @@ -0,0 +1,41 @@ +import nidaqmx +import numpy as np +import time + +# Constantes équivalentes aux macros en C +DAQmx_Val_Rising = nidaqmx.constants.Edge.RISING +DAQmx_Val_CountUp = nidaqmx.constants.CountDirection.COUNT_UP + +def main(): + error = 0 + task_handle = nidaqmx.Task() + data = np.zeros(1, dtype=np.uint32) + + try: + # DAQmx Configure Code + task_handle.ci_channels.add_ci_count_edges_chan("Dev1/ctr0", edge=DAQmx_Val_Rising, initial_count=0, count_direction=DAQmx_Val_CountUp) + + # DAQmx Start Code + task_handle.start() + + print("En cours de lecture en continu. Appuyez sur Ctrl+C pour interrompre\n") + while True: + # DAQmx Read Code + data=task_handle.read(number_of_samples_per_channel=1) + + print("\rCompteur : {}".format(data[0]), end='', flush=True) + time.sleep(0.1) # Intervalle pour éviter une utilisation excessive du processeur + + except Exception as e: + print("\n") + if task_handle.is_task_done() == 0: + task_handle.stop() + task_handle.close() + print("Erreur DAQmx : {}".format(str(e))) + + finally: + print("\nFin du programme. Appuyez sur la touche Entrée pour quitter\n") + input() + +if __name__ == "__main__": + main() diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmx-ni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmx-ni.py deleted file mode 100644 index ea8e5d5..0000000 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmx-ni.py +++ /dev/null @@ -1,712 +0,0 @@ -import nidaqmx -import ctypes -from enum import IntEnum -import numpy as np -from pymodaq.utils.logger import set_logger, get_module_name - -logger = set_logger(get_module_name(__file__)) - - -class DAQ_NIDAQ_source(IntEnum): - """ - Enum class of NIDAQ_source - - =============== ========== - **Attributes** **Type** - *Analog_Input* int - *Counter* int - =============== ========== - """ - Analog_Input = 0 - Counter = 1 - Analog_Output = 2 - Digital_Output = 3 - Digital_Input = 4 - Terminals = 5 - - @classmethod - def names(cls): - return [name for name, member in cls.__members__.items()] - - -class DAQ_analog_types(IntEnum): - """ - Enum class of Ai types - - =============== ========== - **Attributes** **Type** - =============== ========== - """ - Voltage = nidaqmx.constants.UsageTypeAI.VOLTAGE.value - Current = nidaqmx.constants.UsageTypeAI.CURRENT.value - Thermocouple = nidaqmx.constants.UsageTypeAI.TEMPERATURE_THERMOCOUPLE.value - - @classmethod - def names(cls): - return [name for name, member in cls.__members__.items()] - - @classmethod - def values(cls): - return [cls[name].value for name, member in cls.__members__.items()] - - -class DAQ_thermocouples(IntEnum): - """ - Enum class of thermocouples type - - =============== ========== - **Attributes** **Type** - =============== ========== - """ - J = nidaqmx.constants.ThermocoupleType.J.value - K = nidaqmx.constants.ThermocoupleType.K.value - N = nidaqmx.constants.ThermocoupleType.N.value - R = nidaqmx.constants.ThermocoupleType.R.value - S = nidaqmx.constants.ThermocoupleType.S.value - T = nidaqmx.constants.ThermocoupleType.T.value - B = nidaqmx.constants.ThermocoupleType.B.value - E = nidaqmx.constants.ThermocoupleType.E.value - - @classmethod - def names(cls): - return [name for name, member in cls.__members__.items()] - - -class DAQ_termination(IntEnum): - """ - Enum class of thermocouples type - - =============== ========== - **Attributes** **Type** - =============== ========== - """ - Auto = nidaqmx.constants.TerminalConfiguration.DEFAULT.value - RSE = nidaqmx.constants.TerminalConfiguration.RSE.value - NRSE = nidaqmx.constants.TerminalConfiguration.NRSE.value - Diff = nidaqmx.constants.TerminalConfiguration.DIFF.value - Pseudodiff = nidaqmx.constants.TerminalConfiguration.PSEUDO_DIFF.value - - @classmethod - def names(cls): - return [name for name, member in cls.__members__.items()] - - -class Edge(IntEnum): - """ - """ - Rising = nidaqmx.constants.Edge.RISING.value - Falling = nidaqmx.constants.Edge.FALLING.value - - @classmethod - def names(cls): - return [name for name, member in cls.__members__.items()] - - -class ClockMode(IntEnum): - """ - """ - #Finite = PyDAQmx.DAQmx_Val_Rising // These values were not correct? - #Continuous = PyDAQmx.DAQmx_Val_Falling - Finite = nidaqmx.constants.AcquisitionType.FINITE.value - Continuous = nidaqmx.constants.AcquisitionType.CONTINUOUS.value - - @classmethod - def names(cls): - return [name for name, member in cls.__members__.items()] - - -class ClockSettingsBase: - def __init__(self, Nsamples=1000, repetition=False): - - self.Nsamples = Nsamples - self.repetition = repetition - - -class ClockSettings(ClockSettingsBase): - def __init__(self, source=None, frequency=1000, Nsamples=1000, edge=Edge.names()[0], repetition=False): - super().__init__(Nsamples, repetition) - self.source = source - assert edge in Edge.names() - self.frequency = frequency - self.edge = edge - - -class ChangeDetectionSettings(ClockSettingsBase): - def __init__(self, Nsamples=1000, rising_channel='', falling_channel='', - repetition=False): - super().__init__(Nsamples, repetition) - self.rising_channel = rising_channel - self.falling_channel = falling_channel - - -class TriggerSettings: - def __init__(self, trig_source='', enable=False, edge=Edge.names()[0], level=0.1): - assert edge in Edge.names() - self.trig_source = trig_source - self.enable = enable - self.edge = edge - self.level = level - - -class Channel: - def __init__(self, name='', source=DAQ_NIDAQ_source.names()[0]): - """ - Parameters - ---------- - - """ - self.name = name - assert source in DAQ_NIDAQ_source.names() - self.source = source - - -class AChannel(Channel): - def __init__(self, analog_type=DAQ_analog_types.names()[0], value_min=-10., value_max=+10., **kwargs): - """ - Parameters - ---------- - min: (float) minimum value for the configured input channel (could be voltage, amps, temperature...) - max: (float) maximum value for the configured input channel - """ - super().__init__(**kwargs) - self.value_min = value_min - self.value_max = value_max - self.analog_type = analog_type - - -class AIChannel(AChannel): - def __init__(self, termination=DAQ_termination.names()[0], **kwargs): - super().__init__(**kwargs) - assert termination in DAQ_termination.names() - self.termination = termination - - -class AIThermoChannel(AIChannel): - def __init__(self, thermo_type=DAQ_thermocouples.names()[0], **kwargs): - super().__init__(**kwargs) - assert thermo_type in DAQ_thermocouples.names() - self.thermo_type = thermo_type - - -class AOChannel(AChannel): - def __init__(self, **kwargs): - super().__init__(**kwargs) - - -class Counter(Channel): - def __init__(self, edge=Edge.names()[0], **kwargs): - assert edge in Edge.names() - super().__init__(**kwargs) - self.edge = edge - self.counter_type = "Edge Counter" - - -class ClockCounter(Counter): - def __init__(self, clock_frequency, **kwargs): - super().__init__(**kwargs) - self.clock_frequency = clock_frequency - self.counter_type = "Clock Output" - - -class SemiPeriodCounter(Counter): - def __init__(self, value_max, **kwargs): - super().__init__(**kwargs) - self.value_max = value_max - self.counter_type = "SemiPeriod Input" - - -class DigitalChannel(Channel): - def __init__(self, **kwargs): - super().__init__(**kwargs) - - -class DOChannel(DigitalChannel): - def __init__(self, **kwargs): - super().__init__(**kwargs) - - -class DIChannel(DigitalChannel): - def __init__(self, **kwargs): - super().__init__(**kwargs) - - -class DAQmx: - """Wrapper around the PyDAQmx package giving an easy to use object to instantiate channels and tasks""" - def __init__(self): - self.devices = [] - self.channels = [] - self._device = None - self._task = None - self.update_NIDAQ_devices() - self.update_NIDAQ_channels() - self.c_callback = None - self.callback_data = None - self.is_scalar = True - self.write_buffer = np.array([0.]) - - @property - def task(self): - return self._task - - @property - def device(self): - return self._device - - @device.setter - def device(self, device): - if device not in self.devices: - raise IOError(f'your device: {device} is not known or connected') - self._device = device - - def update_NIDAQ_devices(self): - self.devices = self.get_NIDAQ_devices() - - @classmethod - def get_NIDAQ_devices(cls): - """Get list of NI connected devices - - Returns - ------- - list - list of devices as strings to be used in subsequent commands - """ - try: - #string = try_string_buffer(PyDAQmx.DAQmxGetSysDevNames) - devices = nidaqmx.system.System.local().devices.device_names#string.split(', ') - if devices == ['']: - devices = [] - return devices - except: - return [] - - def update_NIDAQ_channels(self, source_type=None): - self.channels = self.get_NIDAQ_channels(self.devices, source_type=source_type) - - @classmethod - def get_NIDAQ_channels(cls, devices=None, source_type=None): - """Get the list of available channels for all NiDAq connected devices - - Parameters - ---------- - devices: list - list of strings, each one being a connected device - source_type: str - One of the entries of DAQ_NIDAQ_source enum - - Returns - ------- - List of str containing device and channel names - - """ - if devices is None: - devices = cls.get_NIDAQ_devices() - - if source_type is None: - source_type = DAQ_NIDAQ_source.names() - if not isinstance(source_type, list): - source_type = [source_type] - channels_tot = [] - if not not devices: - for device in devices: - for source in source_type: - if source == DAQ_NIDAQ_source['Analog_Input'].name: # analog input - string = nidaqmx.system.System.local().devices[device].ai_physical_chans.channel_names - elif source == DAQ_NIDAQ_source['Counter'].name: # counter - string = nidaqmx.system.System.local().devices[device].ci_physical_chans.channel_names - elif source == DAQ_NIDAQ_source['Analog_Output'].name: # analog output - string = nidaqmx.system.System.local().devices[device].ao_physical_chans.channel_names - elif source == DAQ_NIDAQ_source['Digital_Output'].name: # digital output - string = nidaqmx.system.System.local().devices[device].do_lines.channel_names - elif source == DAQ_NIDAQ_source['Digital_Input'].name: # digital iutput - string = nidaqmx.system.System.local().devices[device].di_lines.channel_names - elif source == DAQ_NIDAQ_source['Terminals'].name: # terminals - string = nidaqmx.system.System.local().devices[device].terminals - - channels = string - if channels != ['']: - channels_tot.extend(channels) - - return channels_tot - - @classmethod - def getAOMaxRate(cls, device): - #data = PyDAQmx.c_double() - #PyDAQmx.DAQmxGetDevAOMaxRate(device, PyDAQmx.byref(data)) - return nidaqmx.system.device.Device(device).ao_max_rate - - @classmethod - def getAIMaxRate(cls, device): - #data = PyDAQmx.c_double() - #PyDAQmx.DAQmxGetDevAIMaxSingleChanRate(device, PyDAQmx.byref(data)) - return nidaqmx.system.device.Device(device).ai_max_single_chan_rate - - @classmethod - def isAnalogTriggeringSupported(cls, device): - #data = PyDAQmx.c_uint32() - #PyDAQmx.DAQmxGetDevAnlgTrigSupported(device, PyDAQmx.byref(data)) - return nidaqmx.system.device.Device(device).anlg_trig_supported - - @classmethod - def isDigitalTriggeringSupported(cls, device): - #data = PyDAQmx.c_uint32() - #PyDAQmx.DAQmxGetDevDigTrigSupported(device, PyDAQmx.byref(data)) - return nidaqmx.system.device.Device(device).dig_trig_supported - - @classmethod - def getTriggeringSources(cls, devices=None): - sources = [] - if devices is None: - devices = cls.get_NIDAQ_devices() - - for device in devices: - if cls.isDigitalTriggeringSupported(device): - string = nidaqmx.system.device.Device(device).terminals - channels = [chan for chan in string if 'PFI' in chan] - if channels != ['']: - sources.extend(channels) - if cls.isAnalogTriggeringSupported(device): - channels = nidaqmx.system.device.Device(device).ai_physical_chans.channel_names - if channels != ['']: - sources.extend(channels) - return sources - - def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_settings=TriggerSettings()): - - try: - if self._task is not None: - if isinstance(self._task, nidaqmx.Task): - self._task.ClearTask() - - self._task = None - self.c_callback = None - - self._task = nidaqmx.Task() - - ## create all channels one task for one type of channels - for channel in channels: - if channel.source == 'Analog_Input': #analog input - if channel.analog_type == "Voltage": - err_code = self._task.ai_channels.add_ai_voltage_chan(channel.name, "", - DAQ_termination[channel.termination].value, - channel.value_min, - channel.value_max, - nidaqmx.constants.VoltageUnits.VOLTS, None) - - elif channel.analog_type == "Current": - err_code = self._task.ai_channels.add_ai_current_chan(channel.name, "", - DAQ_termination[channel.termination].value, - channel.value_min, - channel.value_max, - nidaqmx.constants.CurrentUnits.AMPS, - nidaqmx.constants.CurrentShuntResistorLocation.INTERNAL, - 0., None) - - elif channel.analog_type == "Thermocouple": - err_code = self._task.ai_channels.add_ai_thrmcpl_chan(channel.name, "", - channel.value_min, - channel.value_max, - nidaqmx.constants.TemperatureUnits.DEG_C, - DAQ_termination[channel.thermo_type].value, - nidaqmx.constants.CJCSource.BUILT_IN, 0., "") - - elif channel.source == 'Counter': #counter - if channel.counter_type == "Edge Counter": - err_code = self._task.ci_channels.add_ci_count_edges_chan(channel.name, "", - Edge[channel.edge].value, 0, - nidaqmx.constants.CountDirection.COUNT_UP) - elif channel.counter_type == "Clock Output": - err_code = self._task.co_channels.add_co_pulse_chan_freq(channel.name, "clock task", - # units, Hertz in our case - nidaqmx.constants.FrequencyUnits.HZ, - # idle state - nidaqmx.constants.Level.LOW, - # initial delay - 0, - # pulse frequency - channel.clock_frequency, - # duty cycle of pulses, 0.5 such that - # high and low duration are both - # equal to count_interval - 0.5) - elif channel.counter_type == "SemiPeriod Input": - err_code = self._task.ci_channels.add_ci_semi_period_chan(channel.name, "counter task", - 0, # expected min - channel.value_max, # expected max - nidaqmx.constants.TimeUnits.TICKS, "") - - - if not not err_code: - status = self.DAQmxGetErrorString(err_code) - raise IOError(status) - - elif channel.source == 'Analog_Output': # Analog_Output - if channel.analog_type == "Voltage": - err_code = self._task.ao_channels.add_ao_voltage_chan(channel.name, "", - channel.value_min, - channel.value_max, - nidaqmx.constants.VoltageUnits.VOLTS, None) - - if channel.analog_type == "Current": - err_code = self._task.ao_channels.add_ao_current_chan(channel.name, "", - channel.value_min, - channel.value_max, - nidaqmx.constants.VoltageUnits.VOLTS, None) - - elif channel.source == 'Digital_Output': #Digital_Output - err_code = self._task.do_channels.add_do_chan(channel.name, "", - nidaqmx.constants.LineGrouping.CHAN_PER_LINE) - if not not err_code: - status = self.DAQmxGetErrorString(err_code) - raise IOError(status) - - elif channel.source == 'Digital_Input': #Digital_Input - err_code = self._task.di_channels.add_di_chan(channel.name, "", - nidaqmx.constants.LineGrouping.CHAN_PER_LINE) - if not not err_code: - status = self.DAQmxGetErrorString(err_code) - raise IOError(status) - - ## configure the timing - if clock_settings.repetition: - mode = nidaqmx.constants.AcquisitionType.CONTINUOUS - else: - mode = nidaqmx.constants.AcquisitionType.FINITE - if clock_settings.Nsamples > 1 and err_code == 0: - if isinstance(clock_settings, ClockSettings): - err_code =\ - self._task.timing.cfg_samp_clk_timing(clock_settings.source, clock_settings.frequency, - Edge[clock_settings.edge].value, - mode, - clock_settings.Nsamples) - elif isinstance(clock_settings, ChangeDetectionSettings): - err_code =\ - self._task.timing.cfg_change_detection_timing(clock_settings.rising_channel, - clock_settings.falling_channel, - mode, - clock_settings.Nsamples) - - if not not err_code: - status = self.DAQmxGetErrorString(err_code) - raise IOError(status) - - if not trigger_settings.enable: - if channel.source == 'Counter': - pass - else: - err = self._task.DisableStartTrig() - if err != 0: - raise IOError(self.DAQmxGetErrorString(err)) - else: - if 'PF' in trigger_settings.trig_source: - self._task.triggers.start_trigger.disable_start_trig(trigger_settings.trig_source, - Edge[trigger_settings.edge].value) - elif 'ai' in trigger_settings.trig_source: - self._task.triggers.start_trigger.cfg_anlg_edge_start_trig(trigger_settings.trig_source, - Edge[trigger_settings.edge].value, - trigger_settings.level) - else: - raise IOError('Unsupported Trigger source') - - except Exception as e: - print(e) - - def register_callback(self, callback, event='done', nsamples=1): - - if event == 'done': - #self.c_callback = PyDAQmx.DAQmxDoneEventCallbackPtr(callback) - #self._task.register_done_event(0, self.c_callback, None) - self._task.register_done_event(nidaqmx.constants.Signal.SAMPLE_COMPLETE, callback) - #NOT SURE HERE - elif event == 'sample': - #self.c_callback = PyDAQmx.DAQmxSignalEventCallbackPtr(callback) - #self._task.register_signal_event(PyDAQmx.DAQmx_Val_SampleCompleteEvent, 0, self.c_callback, None) - self._task.register_every_n_samples_acquired_into_buffer_event(1, - callback) - elif event == 'Nsamples': - #self.c_callback = PyDAQmx.DAQmxEveryNSamplesEventCallbackPtr(callback) - self._task.register_every_n_samples_acquired_into_buffer_event(nsamples, - callback) - - def get_last_write_index(self): - if self.task is not None: - index_buffer = PyDAQmx.c_uint64() - ret = self._task.GetWriteCurrWritePos(PyDAQmx.byref(index_buffer)) - if not not ret: - raise IOError(self.DAQmxGetErrorString(ret)) - else: - return index_buffer.value - else: - return -1 - - def get_last_write(self): - if self.is_scalar: - return self.write_buffer[-1] - else: - index = self.get_last_write_index() - if index != -1: - return self.write_buffer[index % len(self.write_buffer)] - - else: - return 0. - - def writeAnalog(self, Nsamples, Nchannels, values, autostart=False): - """ - Write Nsamples on N analog output channels - Parameters - ---------- - Nsamples: (int) numver of samples to write on each channel - Nchannels: (int) number of AO channels defined in the task - values: (ndarray) 2D array (or flattened array) of size Nsamples * Nchannels - - Returns - ------- - - """ - if np.prod(values.shape) != Nsamples * Nchannels: - raise ValueError(f'The shape of analog outputs values is incorrect, should be {Nsamples} x {Nchannels}') - - if len(values.shape) != 1: - values = values.reshape((Nchannels * Nsamples)) - self.write_buffer = values - - timeout = -1 - if values.size == 1: - self._task.WriteAnalogScalarF64(autostart, timeout, values[0], None) - self.is_scalar = True - - else: - self.is_scalar = False - read = PyDAQmx.int32() - self._task.WriteAnalogF64(Nsamples, autostart, timeout, PyDAQmx.DAQmx_Val_GroupByChannel, values, - PyDAQmx.byref(read), None) - if read.value != Nsamples: - raise IOError(f'Insufficient number of samples have been written:{read.value}/{Nsamples}') - - def readAnalog(self, Nchannels, clock_settings): - read = PyDAQmx.int32() - N = clock_settings.Nsamples - data = np.zeros(N * Nchannels, dtype=np.float64) - timeout = N * Nchannels * 1 / clock_settings.frequency * 2 # set to twice the time it should take to acquire the data - - self._task.ReadAnalogF64(N, timeout, PyDAQmx.DAQmx_Val_GroupByChannel, data, len(data), - PyDAQmx.byref(read), None) - if read.value == N: - return data - else: - raise IOError(f'Insufficient number of samples have been read:{read.value}/{N}') - - def readCounter(self, Nchannels, counting_time=10., read_function="Ex"): - - data_counter = np.zeros(Nchannels, dtype='uint32') - read = PyDAQmx.int32() - if read_function == "Ex": - self._task.ReadCounterU32Ex(PyDAQmx.DAQmx_Val_Auto, 2*counting_time, - PyDAQmx.DAQmx_Val_GroupByChannel, - data_counter, - Nchannels, PyDAQmx.byref(read), None) - else: - self._task.ReadCounterU32(PyDAQmx.DAQmx_Val_Auto, 2*counting_time, - data_counter, Nchannels, PyDAQmx.byref(read), None) - - self._task.StopTask() - - if read.value == Nchannels: - return data_counter - else: - raise IOError(f'Insufficient number of samples have been read:{read}/{Nchannels}') - - def readDigital(self, Nchannels): - read = PyDAQmx.int32() - bytes_sample = PyDAQmx.int32() - data = np.zeros(Nchannels, dtype='uint8') - self._task.ReadDigitalLines(Nchannels, 0, PyDAQmx.DAQmx_Val_GroupByChannel, - data, Nchannels, PyDAQmx.byref(read), PyDAQmx.byref(bytes_sample), None); - - if read.value == Nchannels: - return data - else: - raise IOError(f'Insufficient number of samples have been read:{read}/{Nchannels}') - - def writeDigital(self, Nchannels, values, autostart=False): - if np.prod(values.shape) != Nchannels: - raise ValueError(f'The shape of digital outputs values is incorrect, should be {Nchannels}') - values.astype(np.uint8) - written = PyDAQmx.int32() - self._task.WriteDigitalLines(Nchannels, autostart, 0, PyDAQmx.DAQmx_Val_GroupByChannel, - values, PyDAQmx.byref(written), None) - if written.value != Nchannels: - raise IOError(f'Insufficient number of samples have been written:{written}/{Nchannels}') - - @classmethod - def getAIVoltageRange(cls, device='Dev1'): - #buff_size = 100 - #ranges = ctypes.pointer((buff_size*ctypes.c_double)()) - #ret = PyDAQmx.DAQmxGetDevAIVoltageRngs(device, ranges[0], buff_size) - ret = nidaqmx.system.System.local().devices[device].ai_voltage_rngs - if ret == 0: - return [tuple(ranges.contents[2*ind:2*(ind+1)]) for ind in range(int(buff_size/2-2)) - if np.abs(ranges.contents[2*ind]) > 1e-12] - return [(-10., 10.)] - - @classmethod - def getAOVoltageRange(cls, device='Dev1'): - #buff_size = 100 - #ranges = ctypes.pointer((buff_size*ctypes.c_double)()) - #ret = PyDAQmx.DAQmxGetDevAOVoltageRngs(device, ranges[0], buff_size) - ret = nidaqmx.system.System.local().devices[device].ao_voltage_rngs - #if ret == 0: - # return [tuple(ranges.contents[2*ind:2*(ind+1)]) for ind in range(int(buff_size/2-2)) - # if np.abs(ranges.contents[2*ind]) > 1e-12] - return [tuple(ret)] #[(-10., 10.)] Why this format is needed - - def stop(self): - if self._task is not None: - self._task.stop() - - def start(self): - if self._task is not None: - self._task.start() - - def close(self): - """ - close the current task. - """ - if self._task is not None: - self._task.stop() - self._task.close() - self._task = None - - @classmethod - def DAQmxGetErrorString(cls, error_code): - if error_code is None: - return '' - else: - #buffer = PyDAQmx.create_string_buffer(1024) - #PyDAQmx.DAQmxGetErrorString(error_code, buffer, len(buffer)) - #return buffer.value.decode() - return nidaqmx.errors.check_for_error(error_code) - def isTaskDone(self): - #done = PyDAQmx.bool32(False) - #self._task.GetTaskComplete(PyDAQmx.byref(done)) - #return bool(done.value) - return task.is_task_done() - def waitTaskDone(self, timeout=10.): - ret = self._task.wait_until_done(timeout) - if ret != 0: - logger.info(self.DAQmxGetErrorString(ret)) - - def refresh_hardware(self): - """ - Refresh the NIDAQ hardware from settings values. - - See Also - -------- - update_NIDAQ_devices, update_NIDAQ_channels - """ - devices = self.update_NIDAQ_devices() - self.update_NIDAQ_channels(devices) - - -if __name__ == '__main__': - print(DAQmx.get_NIDAQ_channels()) - pass diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmx.py index 7ce5fe1..81c972a 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmx.py @@ -105,8 +105,9 @@ def names(cls): class ClockMode(IntEnum): """ """ - Finite = PyDAQmx.DAQmx_Val_Rising - Continuous = PyDAQmx.DAQmx_Val_Falling + Finite = PyDAQmx.DAQmx_Val_FiniteSamps + Continuous = PyDAQmx.DAQmx_Val_ContSamps + @classmethod def names(cls): diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py new file mode 100644 index 0000000..5d1d7cb --- /dev/null +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -0,0 +1,592 @@ +import time + + +from enum import IntEnum +import numpy as np +from pymodaq.utils.logger import set_logger, get_module_name +# from threading import Timer +import nidaqmx +from nidaqmx.constants import * +from nidaqmx.system.device import Device +from nidaqmx.errors import DaqError, DAQmxErrors + + +logger = set_logger(get_module_name(__file__)) + + +class DAQ_NIDAQ_source(IntEnum): + """ + Enum class of NIDAQ_source + + =============== ========== + **Attributes** **Type** + *Analog_Input* int + *Counter* int + =============== ========== + """ + Analog_Input = 0 + Counter = 1 + Analog_Output = 2 + Digital_Output = 3 + Digital_Input = 4 + Terminals = 5 + + @classmethod + def names(cls): + return [name for name, member in cls.__members__.items()] + + +class DAQ_analog_types(IntEnum): + """ + Enum class of Ai types + + =============== ========== + **Attributes** **Type** + =============== ========== + """ + Voltage = UsageTypeAI.VOLTAGE.value + Current = UsageTypeAI.CURRENT.value + Thermocouple = UsageTypeAI.TEMPERATURE_THERMOCOUPLE.value + + @classmethod + def names(cls): + return [name for name, member in cls.__members__.items()] + + @classmethod + def values(cls): + return [cls[name].value for name, member in cls.__members__.items()] + + +class DAQ_thermocouples(IntEnum): + """ + Enum class of thermocouples type + + =============== ========== + **Attributes** **Type** + =============== ========== + """ + J = ThermocoupleType.J.value + K = ThermocoupleType.K.value + N = ThermocoupleType.N.value + R = ThermocoupleType.R.value + S = ThermocoupleType.S.value + T = ThermocoupleType.T.value + B = ThermocoupleType.B.value + E = ThermocoupleType.E.value + + @classmethod + def names(cls): + return [name for name, member in cls.__members__.items()] + + +class DAQ_termination(IntEnum): + """ + Enum class of thermocouples type + + =============== ========== + **Attributes** **Type** + =============== ========== + """ + Auto = TerminalConfiguration.DEFAULT.value + RSE = TerminalConfiguration.RSE.value + NRSE = TerminalConfiguration.NRSE.value + Diff = TerminalConfiguration.DIFF.value + Pseudodiff = TerminalConfiguration.PSEUDO_DIFF.value + + @classmethod + def names(cls): + return [name for name, member in cls.__members__.items()] + + +class Edge(IntEnum): + """ + """ + Rising = Edge.RISING.value + Falling = Edge.FALLING.value + + @classmethod + def names(cls): + return [name for name, member in cls.__members__.items()] + + +class ClockMode(IntEnum): + """ + """ + Finite = AcquisitionType.FINITE.value + Continuous = AcquisitionType.CONTINUOUS.value + + @classmethod + def names(cls): + return [name for name, member in cls.__members__.items()] + + +class ClockSettingsBase: + def __init__(self, Nsamples=1000, repetition=False): + + self.Nsamples = Nsamples + self.repetition = repetition + + +class ClockSettings(ClockSettingsBase): + def __init__(self, source=None, frequency=1000, Nsamples=1000, edge=Edge.names()[0], repetition=False): + super().__init__(Nsamples, repetition) + self.source = source + assert edge in Edge.names() + self.frequency = frequency + self.edge = edge + + +class ChangeDetectionSettings(ClockSettingsBase): + def __init__(self, Nsamples=1000, rising_channel='', falling_channel='', + repetition=False): + super().__init__(Nsamples, repetition) + self.rising_channel = rising_channel + self.falling_channel = falling_channel + + +class TriggerSettings: + def __init__(self, trig_source='', enable=False, edge=Edge.names()[0], level=0.1): + assert edge in Edge.names() + self.trig_source = trig_source + self.enable = enable + self.edge = edge + self.level = level + + +class Channel: + def __init__(self, name='', source=DAQ_NIDAQ_source.names()[0]): + """ + Parameters + ---------- + + """ + self.name = name + assert source in DAQ_NIDAQ_source.names() + self.source = source + + +class AChannel(Channel): + def __init__(self, analog_type=DAQ_analog_types.names()[0], value_min=-10., value_max=+10., **kwargs): + """ + Parameters + ---------- + min: (float) minimum value for the configured input channel (could be voltage, amps, temperature...) + max: (float) maximum value for the configured input channel + """ + super().__init__(**kwargs) + self.value_min = value_min + self.value_max = value_max + self.analog_type = analog_type + + +class AIChannel(AChannel): + def __init__(self, termination=DAQ_termination.names()[0], **kwargs): + super().__init__(**kwargs) + assert termination in DAQ_termination.names() + self.termination = termination + + +class AIThermoChannel(AIChannel): + def __init__(self, thermo_type=DAQ_thermocouples.names()[0], **kwargs): + super().__init__(**kwargs) + assert thermo_type in DAQ_thermocouples.names() + self.thermo_type = thermo_type + + +class Counter(Channel): + def __init__(self, edge=Edge.names()[0], **kwargs): + assert edge in Edge.names() + super().__init__(**kwargs) + self.edge = edge + self.counter_type = "Edge Counter" + + +class ClockCounter(Counter): + def __init__(self, clock_frequency=100, **kwargs): + super().__init__(**kwargs) + self.clock_frequency = clock_frequency + self.counter_type = "Clock Output" + + +class DAQmx: + """Wrapper around the PyDAQmx package giving an easy to use object to instantiate channels and tasks""" + def __init__(self): + self.devices = [] + self.channels = [] + self._device = None + self._task = None + self.update_NIDAQ_devices() + self.update_NIDAQ_channels() + self.c_callback = None # A qui servent ces callback ?? + self.callback_data = None + self.is_scalar = True + self.write_buffer = np.array([0.]) # ou est utilisé ce buffer?? + + @property + def task(self): + return self._task + + @property + def device(self): + return self._device + + @device.setter + def device(self, device): + if device not in self.devices: + raise IOError(f'your device: {device} is not known or connected') + self._device = device + + def update_NIDAQ_devices(self): + self.devices = self.get_NIDAQ_devices() + + @classmethod + def get_NIDAQ_devices(cls): + """Get list of NI connected devices + + Returns + ------- + list + list of devices as strings to be used in subsequent commands + """ + try: + devices = nidaqmx.system.System.local().devices.device_names + if devices == ['']: + devices = [] + return devices + except DaqError as e: + return e.error_code + + def update_NIDAQ_channels(self, source_type=None): + self.channels = self.get_NIDAQ_channels(self.devices, source_type=source_type) + + @classmethod + def get_NIDAQ_channels(cls, devices=None, source_type=None): + """Get the list of available channels for all NiDAq connected devices + + Parameters + ---------- + devices: list + list of strings, each one being a connected device + source_type: str + One of the entries of DAQ_NIDAQ_source enum + + Returns + ------- + List of str containing device and channel names + + """ + if devices is None: + devices = cls.get_NIDAQ_devices() + + if source_type is None: + source_type = DAQ_NIDAQ_source.names() + if not isinstance(source_type, list): + source_type = [source_type] + channels_tot = [] + channels = [] + if not not devices: + for device in devices: + for source in source_type: + if source == DAQ_NIDAQ_source['Analog_Input'].name: # analog input + channels = Device(device).ai_physical_chans.channel_names + elif source == DAQ_NIDAQ_source['Counter'].name: # counter + channels = Device(device).ci_physical_chans.channel_names + elif source == DAQ_NIDAQ_source['Analog_Output'].name: # analog output + channels = Device(device).ao_physical_chans.channel_names + elif source == DAQ_NIDAQ_source['Digital_Output'].name: # digital output + channels = Device(device).do_lines.channel_names + elif source == DAQ_NIDAQ_source['Digital_Input'].name: # digital iutput + channels = Device(device).di_lines.channel_names + elif source == DAQ_NIDAQ_source['Terminals'].name: # terminals + channels = Device(device).terminals + + if channels != ['']: + channels_tot.extend(channels) + + return channels_tot + + @classmethod + def getAOMaxRate(cls, device): + return Device(device).ao_max_rate + + @classmethod + def getAIMaxRate(cls, device): + return Device(device).ai_max_single_chan_rate + + @classmethod + def isAnalogTriggeringSupported(cls, device): + return Device(device).anlg_trig_supported + + @classmethod + def isDigitalTriggeringSupported(cls, device): + return Device(device).dig_trig_supported + + @classmethod + def getTriggeringSources(cls, devices=None): + sources = [] + if devices is None: + devices = cls.get_NIDAQ_devices() + + for device in devices: + if cls.isDigitalTriggeringSupported(device): + string = Device(device).terminals + channels = [chan for chan in string if 'PFI' in chan] + if channels != ['']: + sources.extend(channels) + if cls.isAnalogTriggeringSupported(device): + channels = Device(device).ai_physical_chans.channel_names + if channels != ['']: + sources.extend(channels) + return sources + + def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_settings=TriggerSettings()): + + try: + if self._task is not None: + if isinstance(self._task, nidaqmx.Task): + self._task.close() + + self._task = None + self.c_callback = None + + self._task = nidaqmx.Task() + err_code = None + + # create all channels one task for one type of channels + for channel in channels: + if channel.source == 'Analog_Input': # analog input + try: + if channel.analog_type == "Voltage": + self._task.ai_channels.add_ai_voltage_chan(channel.name, "", + DAQ_termination[channel.termination], + channel.value_min, + channel.value_max, + VoltageUnits.VOLTS, None) + + elif channel.analog_type == "Current": + self._task.ai_channels.add_ai_current_chan(channel.name, "", + DAQ_termination[channel.termination], + channel.value_min, + channel.value_max, + CurrentUnits.AMPS, + CurrentShuntResistorLocation.INTERNAL, + 0., None) + + elif channel.analog_type == "Thermocouple": + self._task.ai_channels.add_ai_thrmcpl_chan(channel.name, "", + channel.value_min, + channel.value_max, + TemperatureUnits.DEG_C, + DAQ_termination[channel.thermo_type], + CJCSource.BUILT_IN, 0., "") + except DaqError as e: + err_code = e.error_code + if not not err_code: + status = self.DAQmxGetErrorString(err_code) + raise IOError(status) + elif channel.source == 'Counter': # counter + try: + if channel.counter_type == "Edge Counter": + self._task.ci_channels.add_ci_count_edges_chan(channel.name, "", + Edge[channel.edge], 0, + CountDirection.COUNT_UP) + + elif channel.counter_type == "Clock Output": + self._task.co_channels.add_co_pulse_chan_freq(channel.name, "clock task", + FrequencyUnits.HZ, + Level.LOW, + 0, + channel.clock_frequency, + 0.5) + + elif channel.counter_type == "SemiPeriod Input": + self._task.ci_channels.add_ci_semi_period_chan(channel.name, "counter task", + 0, # expected min + channel.value_max, # expected max + TimeUnits.TICKS, "") + + except DaqError as e: + err_code = e.error_code + + if not not err_code: + status = self.DAQmxGetErrorString(err_code) + raise IOError(status) + + if not not err_code: + status = self.DAQmxGetErrorString(err_code) + raise IOError(status) + elif channel.source == 'Analog_Output': # Analog_Output + try: + if channel.analog_type == "Voltage": + self._task.ao_channels.add_ao_voltage_chan(channel.name, "", + channel.value_min, + channel.value_max, + VoltageUnits.VOLTS, None) + + elif channel.analog_type == "Current": + self._task.ao_channels.add_ao_current_chan(channel.name, "", + channel.value_min, + channel.value_max, + VoltageUnits.VOLTS, None) + except DaqError as e: + err_code = e.error_code + if not not err_code: + status = self.DAQmxGetErrorString(err_code) + raise IOError(status) + elif channel.source == 'Digital_Output': + try: + self._task.do_channels.add_do_chan(channel.name, "", + LineGrouping.CHAN_PER_LINE) + except DaqError as e: + err_code = e.error_code + if not not err_code: + status = self.DAQmxGetErrorString(err_code) + raise IOError(status) + elif channel.source == 'Digital_Input': # Digital_Input + try: + self._task.di_channels.add_di_chan(channel.name, "", + LineGrouping.CHAN_PER_LINE) + except DaqError as e: + err_code = e.error_code + if not not err_code: + status = self.DAQmxGetErrorString(err_code) + raise IOError(status) + + # configure the timing + if clock_settings.repetition: + mode = AcquisitionType.CONTINUOUS + else: + mode = AcquisitionType.FINITE + if clock_settings.Nsamples > 1 and err_code == 0: + try: + if isinstance(clock_settings, ClockSettings): + self._task.timing.cfg_samp_clk_timing(clock_settings.source, clock_settings.frequency, + Edge[clock_settings.edge].value, + mode, + clock_settings.Nsamples) + + elif isinstance(clock_settings, ChangeDetectionSettings): + self._task.timing.cfg_change_detection_timing(clock_settings.rising_channel, + clock_settings.falling_channel, + mode, + clock_settings.Nsamples) + except DaqError as e: + err_code = e.error_code + if not not err_code: + status = self.DAQmxGetErrorString(err_code) + raise IOError(status) + + for channel in channels: + if not trigger_settings.enable: + if channel.source == 'Counter': + pass # Maybe here adding the configuration fastCTr0 with Ctr1 etc...? + else: + err = self._task.triggers.start_trigger.disable_start_trig() + if err != 0: + raise IOError(self.DAQmxGetErrorString(err)) + else: + if 'PF' in trigger_settings.trig_source: + self._task.triggers.start_trigger.disable_start_trig() + elif 'ai' in trigger_settings.trig_source: + self._task.triggers.start_trigger.cfg_anlg_edge_start_trig( + trigger_settings.trig_source, + Edge[trigger_settings.edge], + trigger_settings.level) + else: + raise IOError('Unsupported Trigger source') + + except Exception as e: + print(e) + + def register_callback(self, callback, event='done', nsamples=1): + + if event == 'done': + self._task.register_done_event(Signal.SAMPLE_COMPLETE, callback) + # NOT SURE HERE + elif event == 'sample': + self._task.register_every_n_samples_acquired_into_buffer_event(1, + callback) + elif event == 'Nsamples': + self._task.register_every_n_samples_acquired_into_buffer_event(nsamples, + callback) + + def readCounter(self): + # return 25 + + time.sleep(1) + self.value = self._task.read() + + # t = Timer(counting_time,self.read) + # self._task.start() + # t.start() + # time.sleep(2*counting_time) + # print(self.value) + return self.value + + @classmethod + def getAIVoltageRange(cls, device='Dev1'): + ret = nidaqmx.system.System.local().devices[device].ai_voltage_rngs + return [tuple(ret[6:8])] + + @classmethod + def getAOVoltageRange(cls, device='Dev1'): + ret = nidaqmx.system.System.local().devices[device].ao_voltage_rngs + return [tuple(ret)] # [(-10., 10.)] Why this format is needed?? + + def stop(self): + if self._task is not None: + self._task.stop() + + def start(self): + if self._task is not None: + self._task.start() + + def close(self): + """ + close the current task. + """ + if self._task is not None: + self._task.stop() + self._task.close() + self._task = None + + @classmethod + def DAQmxGetErrorString(cls, error_code): + if error_code is None: + return '' + else: + return DAQmxErrors(error_code).name + + def isTaskDone(self): + return self._task.is_task_done() + + def waitTaskDone(self, timeout=10.): + ret = self._task.wait_until_done(timeout) + if ret != 0: + logger.info(self.DAQmxGetErrorString(ret)) + + def refresh_hardware(self): + """ + Refresh the NIDAQ hardware from settings values. + + See Also + -------- + update_NIDAQ_devices, update_NIDAQ_channels + """ + devices = self.update_NIDAQ_devices() + self.update_NIDAQ_channels(devices) + + +if __name__ == '__main__': + print(DAQmx.get_NIDAQ_channels()) + controller = DAQmx() + ch_counter = Counter(name='Dev1/ctr0', + source='Counter', + edge=Edge.names()[0]) + controller.update_task([ch_counter]) + controller.start() + print(controller.readCounter()) + controller.stop() + controller.close() + + pass From 9919deee6ebe9b9c0facf8529d7a02d2fd5a74dd Mon Sep 17 00:00:00 2001 From: Sebastien Date: Tue, 17 Sep 2024 15:26:09 +0200 Subject: [PATCH 07/57] NIDAQmx structure --- src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_NIDAQmx.py | 0 .../daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py | 0 .../hardware/national_instruments/daqmxni.py | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_NIDAQmx.py create mode 100644 src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py diff --git a/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_NIDAQmx.py b/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_NIDAQmx.py new file mode 100644 index 0000000..e69de29 diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py new file mode 100644 index 0000000..e69de29 diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index 5d1d7cb..84fc60a 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -209,7 +209,7 @@ def __init__(self, clock_frequency=100, **kwargs): class DAQmx: - """Wrapper around the PyDAQmx package giving an easy to use object to instantiate channels and tasks""" + """Wrapper around the NIDAQmx package giving an easy to use object to instantiate channels and tasks""" def __init__(self): self.devices = [] self.channels = [] From ab58aea0c986e1e2affbc4ae729ef6f2318b8e08 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Thu, 19 Sep 2024 15:33:54 +0200 Subject: [PATCH 08/57] New daq_viewer_NIDAQmx + wrapper modifs --- .../plugins_0D/daq_0Dviewer_NIDAQmx.py | 253 ++++++++++++++++++ .../hardware/national_instruments/daqmxni.py | 34 ++- 2 files changed, 286 insertions(+), 1 deletion(-) diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py index e69de29..2c2fb5b 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py @@ -0,0 +1,253 @@ +import numpy as np +from pymodaq.utils.daq_utils import ThreadCommand +from pymodaq.utils.data import DataWithAxes, DataToExport, DataSource, DataFromPlugins +from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters, main +from pymodaq.utils.parameter import Parameter +from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import * +from pymodaq.utils.logger import set_logger, get_module_name +logger = set_logger(get_module_name(__file__)) + + +class DAQ_0DViewer_NIDAQmx(DAQ_Viewer_base): + """ + Plugin for a 0D data visualization & acquisition with various NI modules plugged in a NI cDAQ. + """ + + params = comon_parameters+[ + {'title': 'Display type:', 'name': 'display', 'type': 'list', 'limits': ['0D', '1D']}, + {'title': 'Module ref. :', 'name': 'module', 'type': 'list', 'limits': DAQmx.get_NIDAQ_devices(), + 'value': DAQmx.get_NIDAQ_devices()[0] + }, + {'title': 'Devices', 'name': 'devices', 'type': 'text', 'value': ''}, + {'title': 'Channels', 'name': 'channels', 'type': 'text', 'value': ''}, + {'title': 'Analog channel :', 'name': 'ai_channel', 'type': 'list', + 'limits': DAQmx.get_NIDAQ_channels(source_type='Analog_Input'), + 'value': DAQmx.get_NIDAQ_channels(source_type='Analog_Input')[0] + }, + {'title': 'Analog Source :', 'name': 'ai_srce', 'type': 'list', + 'limits': DAQ_NIDAQ_source.names(), + 'value': DAQ_NIDAQ_source.names()[0] + }, + {'title': 'Analog Type :', 'name': 'ai_type', 'type': 'list', + 'limits': DAQ_analog_types.names(), + 'value': DAQ_analog_types.names()[2] + }, + {'title': 'Min. value:', 'name': 'ai_min', 'type': 'float', 'value': -80e-3, 'min': -1e4}, + {'title': 'Max. value:', 'name': 'ai_max', 'type': 'float', 'value': 80e-3, 'max': 1e4}, + {'title': 'Frequency Acq.:', 'name': 'frequency', 'type': 'int', 'value': 4, 'min': 1}, + {'title': 'Nsamples:', 'name': 'Nsamples', 'type': 'int', 'value': 2, 'default': 100, 'min': 1}, + {"title": "Counting channel:", "name": "counter_channel", + "type": "list", "limits": DAQmx.get_NIDAQ_channels(source_type="Counter"), + # 'value': DAQmx.get_NIDAQ_channels(source_type='Counter')[0] + }, + ] + + def __init__(self, parent=None, params_state=None): + super().__init__(parent, params_state) + + def ini_attributes(self): + self.controller: DAQmx = None + self.channels_ai = None + self.task = None + self.live = False # True during a continuous grab + + + def commit_settings(self, param: Parameter): + """Apply the consequences of a change of value in the detector settings + + Parameters + ---------- + param: Parameter + A given parameter (within detector_settings) whose value has been changed by the user + """ + self.update_tasks() + + def ini_detector(self, controller=None): + """Detector communication initialization + + Parameters + ---------- + controller: (object) + custom object of a PyMoDAQ plugin (Slave case). None if only one actuator/detector by controller + (Master case) + + Returns + ------- + info: str + initialized: bool + False if initialization failed otherwise True + """ + logger.info("Detector 0D initialized") + + try: + self.controller = self.ini_detector_init(controller, DAQmx()) + self.update_tasks() + initialized = True + info = "DAQ_0D initialized" + self.controller.update_NIDAQ_devices() + self.controller.update_NIDAQ_channels() + devices = "" + channels = "" + for device in self.controller.devices: + devices += device + " // " + for channel in self.controller.channels: + channels += channel + " // " + self.settings.child('devices').setValue(devices[:-4]) + self.settings.child('channels').setValue(channels[:-4]) + except Exception as e: + print(e) + initialized = False + info = "Error" + + return info, initialized + + def close(self): + """Terminate the communication protocol""" + self.task.close() + self.controller.close() + pass + + def grab_data(self, Naverage=1, **kwargs): + """Start a grab from the detector + + Parameters + ---------- + Naverage: int + Number of hardware averaging not relevant here. + kwargs: dict + others optionals arguments + """ + self.update_tasks() + self.emit_data(self.read_data()) + self.task.close() + + def read_data(self): + + get_data = self.task.read() + + return get_data + + def emit_data(self, data): + print("DATA = ", data) + print("type = ", type(data)) + print("DATA array = ", np.array([data])) + dte = DataToExport(name='NIDAQmx', + data=[DataFromPlugins(name='NI Analog Input', + data=np.array([data]), + # dim=f'Data{self.settings.child("display").value()}', + dim=f'Data0D', + # labels=self.channels_ai[0].name, + # labels='test', + )]) + self.dte_signal.emit(dte) + + def stop(self): + """Stop the current grab hardware wise if necessary""" + self.task.stop() + self.close() + self.emit_status(ThreadCommand('Update_Status', ['Acquisition stopped.'])) + return '' + + def update_tasks(self): + """Set up the counting tasks in the NI card.""" + + + # Create channels + self.channels_ai = [AIThermoChannel(name="cDAQ1Mod1/ai0", + source='Analog_Input', + analog_type='Thermocouple', + value_min=-80.0e-3, + value_max=80.0e-3, + termination='Diff', + thermo_type=DAQ_thermocouples.K), + ] + + self.clock_settings_ai = ClockSettings(frequency=self.settings.child('frequency').value(), + Nsamples=self.settings.child('Nsamples').value(), + repetition=self.live) + + # self.controller.update_task(self.channels_ai, self.clock_settings_ai) + + self.task = nidaqmx.Task() + self.task.ai_channels.add_ai_thrmcpl_chan(physical_channel=self.channels_ai[0].name, + # name_to_assign_to_channel="Channel 01", + min_val=self.channels_ai[0].value_min, + max_val=self.channels_ai[0].value_max, + units=TemperatureUnits.DEG_C, + thermocouple_type=self.channels_ai[0].thermo_type, + # cjc_source=CJCSource.BUILT_IN, + # cjc_val="", + # cjc_channel="", + ) + +if __name__ == '__main__': + # main(__file__) + try: + print("In main") + import nidaqmx + + channels = [AIThermoChannel(name="cDAQ1Mod1/ai0", + source='Analog_Input', analog_type='Thermocouple', + value_min=-80.0e-3, value_max=80.0e-3, + termination='Diff', thermo_type=DAQ_thermocouples.K), + AIThermoChannel(name="cDAQ1Mod1/ai1", + source='Analog_Input', analog_type='Thermocouple', + value_min=-80.0e-3, value_max=80.0e-3, + termination='Diff', thermo_type=DAQ_thermocouples.K), + AIThermoChannel(name="cDAQ1Mod1/ai2", + source='Analog_Input', analog_type='Thermocouple', + value_min=-80.0e-3, value_max=80.0e-3, + termination='Diff', thermo_type=DAQ_thermocouples.K), + AIThermoChannel(name="cDAQ1Mod1/ai3", + source='Analog_Input', analog_type='Thermocouple', + value_min=-80.0e-3, value_max=80.0e-3, + termination='Diff', thermo_type=DAQ_thermocouples.K), + ] + # Create Task + print("DAQ_thermocouples.names ", DAQ_thermocouples.names()) + print("DAQ_thermocouples.K ", DAQ_thermocouples.K) + print("channel.thermo_type ", channels[0].thermo_type) + task = nidaqmx.Task() + for channel in channels: + task.ai_channels.add_ai_thrmcpl_chan(physical_channel=channel.name, + # name_to_assign_to_channel="Channel 01", + min_val=channel.value_min, + max_val=channel.value_max, + units=TemperatureUnits.DEG_C, + thermocouple_type=channel.thermo_type, + # cjc_source=CJCSource.BUILT_IN, + # cjc_val="", + # cjc_channel="", + ) + NIDAQ_Devices = nidaqmx.system.System.local().devices + + print("NIDAQ devices ", NIDAQ_Devices) + print("NIDAQ devices names ", NIDAQ_Devices.device_names) + + Chassis = NIDAQ_Devices[0] + Module01 = NIDAQ_Devices[1] + Module02 = NIDAQ_Devices[2] + print("Chassis={}, Module01={}, Module02={}" .format(Chassis, Module01, Module02)) + + # Test resources + try: + Chassis.self_test_device() + Module01.self_test_device() + Module02.self_test_device() + except Exception as e: + print("Resources test failed: {}" .format(e)) + + print("Chassis: name={}, Num={}".format(Chassis.name, Chassis.product_type)) + print("Module01: name={}, Num={}".format(Module01.name, Module01.product_type)) + print("Module02: name={}, Num={}".format(Module02.name, Module02.product_type)) + + print("channel 01 name : ", channels[0].name) + data = task.read() + print("data = ", data) + print("type(data) = ", type(data)) + print("type(data[0]) = ", type(data[0])) + + task.close() + + except Exception as e: + print("Exception ({}): {}".format(type(e), str(e))) \ No newline at end of file diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index 84fc60a..3522de3 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -189,10 +189,15 @@ def __init__(self, termination=DAQ_termination.names()[0], **kwargs): class AIThermoChannel(AIChannel): def __init__(self, thermo_type=DAQ_thermocouples.names()[0], **kwargs): super().__init__(**kwargs) - assert thermo_type in DAQ_thermocouples.names() + # assert thermo_type in DAQ_thermocouples.names() self.thermo_type = thermo_type +class AOChannel(AChannel): + def __init__(self, **kwargs): + super().__init__(**kwargs) + + class Counter(Channel): def __init__(self, edge=Edge.names()[0], **kwargs): assert edge in Edge.names() @@ -208,6 +213,21 @@ def __init__(self, clock_frequency=100, **kwargs): self.counter_type = "Clock Output" +class DigitalChannel(Channel): + def __init__(self, **kwargs): + super().__init__(**kwargs) + + +class DOChannel(DigitalChannel): + def __init__(self, **kwargs): + super().__init__(**kwargs) + + +class DIChannel(DigitalChannel): + def __init__(self, **kwargs): + super().__init__(**kwargs) + + class DAQmx: """Wrapper around the NIDAQmx package giving an easy to use object to instantiate channels and tasks""" def __init__(self): @@ -509,6 +529,18 @@ def register_callback(self, callback, event='done', nsamples=1): elif event == 'Nsamples': self._task.register_every_n_samples_acquired_into_buffer_event(nsamples, callback) + def readAnalog(self, Nchannels, clock_settings): + read = nidaqmx.int32() + N = clock_settings.Nsamples + data = np.zeros(N * Nchannels, dtype=np.float64) + timeout = N * Nchannels * 1 / clock_settings.frequency * 2 # set to twice the time it should take to acquire the data + + self._task.ReadAnalogF64(N, timeout, nidaqmx.DAQmx_Val_GroupByChannel, data, len(data), + nidaqmx.byref(read), None) + if read.value == N: + return data + else: + raise IOError(f'Insufficient number of samples have been read:{read.value}/{N}') def readCounter(self): # return 25 From 2742e2755ab18ae5f8f14cfab780663a8f3da3f7 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Thu, 26 Sep 2024 15:48:54 +0200 Subject: [PATCH 09/57] code adapted to nidaqmx + live visu ok --- .../plugins_0D/daq_0Dviewer_NIDAQmx.py | 299 ++++++++++-------- .../hardware/national_instruments/daqmxni.py | 118 +++++-- 2 files changed, 252 insertions(+), 165 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py index 2c2fb5b..670bf9c 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py @@ -7,11 +7,14 @@ from pymodaq.utils.logger import set_logger, get_module_name logger = set_logger(get_module_name(__file__)) - class DAQ_0DViewer_NIDAQmx(DAQ_Viewer_base): """ Plugin for a 0D data visualization & acquisition with various NI modules plugged in a NI cDAQ. """ + channels_ai: str + clock_settings_ai: str + dict_device_input: dict + live: bool params = comon_parameters+[ {'title': 'Display type:', 'name': 'display', 'type': 'list', 'limits': ['0D', '1D']}, @@ -30,16 +33,18 @@ class DAQ_0DViewer_NIDAQmx(DAQ_Viewer_base): }, {'title': 'Analog Type :', 'name': 'ai_type', 'type': 'list', 'limits': DAQ_analog_types.names(), - 'value': DAQ_analog_types.names()[2] + 'value': DAQ_analog_types.names()[0] }, {'title': 'Min. value:', 'name': 'ai_min', 'type': 'float', 'value': -80e-3, 'min': -1e4}, {'title': 'Max. value:', 'name': 'ai_max', 'type': 'float', 'value': 80e-3, 'max': 1e4}, - {'title': 'Frequency Acq.:', 'name': 'frequency', 'type': 'int', 'value': 4, 'min': 1}, - {'title': 'Nsamples:', 'name': 'Nsamples', 'type': 'int', 'value': 2, 'default': 100, 'min': 1}, - {"title": "Counting channel:", "name": "counter_channel", - "type": "list", "limits": DAQmx.get_NIDAQ_channels(source_type="Counter"), - # 'value': DAQmx.get_NIDAQ_channels(source_type='Counter')[0] - }, + {'title': 'Clock settings:', 'name': 'clock_settings', 'type': 'group', 'children': [ + {'title': 'Frequency Acq.:', 'name': 'frequency', 'type': 'int', 'value': 3, 'min': 1}, + {'title': 'Nsamples:', 'name': 'Nsamples', 'type': 'int', 'value': 10, 'default': 100, 'min': 1}, + {'title': 'Acquisition mode:', 'name': 'acqmode', 'type': 'list', + 'limits': ['Continuous', 'Finite'], 'value': 'Continous'}, + {"title": "Counting channel:", "name": "counter_channel", "type": "list", + "limits": DAQmx.get_NIDAQ_channels(source_type="Counter")}, + ]}, ] def __init__(self, parent=None, params_state=None): @@ -48,10 +53,10 @@ def __init__(self, parent=None, params_state=None): def ini_attributes(self): self.controller: DAQmx = None self.channels_ai = None - self.task = None + self.clock_settings_ai = None + self.dict_device_input = None self.live = False # True during a continuous grab - def commit_settings(self, param: Parameter): """Apply the consequences of a change of value in the detector settings @@ -60,6 +65,7 @@ def commit_settings(self, param: Parameter): param: Parameter A given parameter (within detector_settings) whose value has been changed by the user """ + # if param.name() == "frequency": self.update_tasks() def ini_detector(self, controller=None): @@ -86,24 +92,18 @@ def ini_detector(self, controller=None): info = "DAQ_0D initialized" self.controller.update_NIDAQ_devices() self.controller.update_NIDAQ_channels() - devices = "" - channels = "" - for device in self.controller.devices: - devices += device + " // " - for channel in self.controller.channels: - channels += channel + " // " - self.settings.child('devices').setValue(devices[:-4]) - self.settings.child('channels').setValue(channels[:-4]) - except Exception as e: - print(e) + logger.info("Detected devices: {}".format(self.controller.devices)) + self.settings.child('devices').setValue(str(self.controller.devices)) + logger.info("Detected channels: {}".format(self.controller.channels)) + self.settings.child('channels').setValue(str(self.controller.channels)) + except Exception as err: + logger.error(err) initialized = False info = "Error" - return info, initialized def close(self): """Terminate the communication protocol""" - self.task.close() self.controller.close() pass @@ -118,136 +118,175 @@ def grab_data(self, Naverage=1, **kwargs): others optionals arguments """ self.update_tasks() - self.emit_data(self.read_data()) - self.task.close() - def read_data(self): - - get_data = self.task.read() - - return get_data - - def emit_data(self, data): - print("DATA = ", data) - print("type = ", type(data)) - print("DATA array = ", np.array([data])) + data_from_task = self.controller._task.read() + logger.info("DATA = {}".format(data_from_task)) + + self.emit_data(data_from_task) + + def emit_data(self, data_measurement): + logger.info("OOOOOOOOOOOOO {}".format([chan.name for chan in self.channels_ai])) + # dte = DataToExport(name='NIDAQmx', + # data=[DataFromPlugins(name='NI Analog Input 01', + # data=[np.array([data_measurement[0]])], + # # dim=f'Data{self.settings.child("display").value()}', + # dim=f'Data0D', + # labels=[self.channels_ai[0].name], + # ), + # ]) dte = DataToExport(name='NIDAQmx', - data=[DataFromPlugins(name='NI Analog Input', - data=np.array([data]), + data=[DataFromPlugins(name='NI Analog Input 02', + data=[np.array([data_measurement[2]])], # dim=f'Data{self.settings.child("display").value()}', dim=f'Data0D', - # labels=self.channels_ai[0].name, - # labels='test', - )]) + labels=[self.channels_ai[2].name], + ), + ]) + + # # Dictionary linking devices to channel's physical quantities + # self.dict_device_input = {self.controller.devices[1]: [self.channels_ai[0].name, self.channels_ai[1].name], + # self.controller.devices[3]: self.channels_ai[2].name} + # # self.controller.devices #ci_meas_types + # logger.info("DICT DEVICE LABEL {}".format(self.dict_device_input)) + # logger.info(self.dict_device_input.get(self.controller.devices[1])) + # a = 0 + # b = 0 + # gen = [[ai for ai in self.dict_device_input.get(device)] + # for device in self.dict_device_input if not isinstance(self.dict_device_input.get(device), str)] + \ + # [self.dict_device_input.get(device) + # for device in self.dict_device_input if isinstance(self.dict_device_input.get(device), str)] + # + # logger.info("DATA TO EXPORT = {}".format(gen)) + # + # + # + # dte = DataToExport(name='NIDAQmx', + # data=[DataFromPlugins(name=self.dict_device_input[device], + # data=[np.array([data_measurement[i]]) + # for i in range(len(data_measurement))], + # dim='Data0D', + # labels=[ai for ai in self.dict_device_input.get(device)] + # ) for device in self.dict_device_input]) self.dte_signal.emit(dte) def stop(self): """Stop the current grab hardware wise if necessary""" - self.task.stop() self.close() self.emit_status(ThreadCommand('Update_Status', ['Acquisition stopped.'])) return '' def update_tasks(self): - """Set up the counting tasks in the NI card.""" - - + """Set up the tasks in the NI card.""" # Create channels + ##################### + ''' + Idée: + for Device in Devices: + self.channels_from_device(Device01) = [AIchannel(),AIchannel()......] + self.channels_from_device(Device02) = [AIthermochannel(),AIthermochannel()......] + Emit by mode (as for keithley) === by device + + ''' self.channels_ai = [AIThermoChannel(name="cDAQ1Mod1/ai0", source='Analog_Input', analog_type='Thermocouple', - value_min=-80.0e-3, - value_max=80.0e-3, - termination='Diff', - thermo_type=DAQ_thermocouples.K), + value_min=-20., + value_max=1300., + thermo_type=DAQ_thermocouples.K, + ), + AIThermoChannel(name="cDAQ1Mod1/ai1", + source='Analog_Input', + analog_type='Thermocouple', + value_min=-20., + value_max=1300., + thermo_type=DAQ_thermocouples.K, + ), + AIChannel(name="cDAQ1Mod3/ai0", + source='Analog_Input', + analog_type='Voltage', + value_min=-10., + value_max=10., + termination=DAQ_termination.Diff, + ), ] - - self.clock_settings_ai = ClockSettings(frequency=self.settings.child('frequency').value(), - Nsamples=self.settings.child('Nsamples').value(), + self.clock_settings_ai = ClockSettings(frequency=self.settings.child('clock_settings', 'frequency').value(), + Nsamples=self.settings.child('clock_settings', 'Nsamples').value(), + edge=Edge.Rising, repetition=self.live) - # self.controller.update_task(self.channels_ai, self.clock_settings_ai) + self.controller.update_task(self.channels_ai, self.clock_settings_ai) - self.task = nidaqmx.Task() - self.task.ai_channels.add_ai_thrmcpl_chan(physical_channel=self.channels_ai[0].name, - # name_to_assign_to_channel="Channel 01", - min_val=self.channels_ai[0].value_min, - max_val=self.channels_ai[0].value_max, - units=TemperatureUnits.DEG_C, - thermocouple_type=self.channels_ai[0].thermo_type, - # cjc_source=CJCSource.BUILT_IN, - # cjc_val="", - # cjc_channel="", - ) if __name__ == '__main__': - # main(__file__) - try: - print("In main") - import nidaqmx - - channels = [AIThermoChannel(name="cDAQ1Mod1/ai0", - source='Analog_Input', analog_type='Thermocouple', - value_min=-80.0e-3, value_max=80.0e-3, - termination='Diff', thermo_type=DAQ_thermocouples.K), - AIThermoChannel(name="cDAQ1Mod1/ai1", - source='Analog_Input', analog_type='Thermocouple', - value_min=-80.0e-3, value_max=80.0e-3, - termination='Diff', thermo_type=DAQ_thermocouples.K), - AIThermoChannel(name="cDAQ1Mod1/ai2", - source='Analog_Input', analog_type='Thermocouple', - value_min=-80.0e-3, value_max=80.0e-3, - termination='Diff', thermo_type=DAQ_thermocouples.K), - AIThermoChannel(name="cDAQ1Mod1/ai3", - source='Analog_Input', analog_type='Thermocouple', - value_min=-80.0e-3, value_max=80.0e-3, - termination='Diff', thermo_type=DAQ_thermocouples.K), - ] - # Create Task - print("DAQ_thermocouples.names ", DAQ_thermocouples.names()) - print("DAQ_thermocouples.K ", DAQ_thermocouples.K) - print("channel.thermo_type ", channels[0].thermo_type) - task = nidaqmx.Task() - for channel in channels: - task.ai_channels.add_ai_thrmcpl_chan(physical_channel=channel.name, - # name_to_assign_to_channel="Channel 01", - min_val=channel.value_min, - max_val=channel.value_max, - units=TemperatureUnits.DEG_C, - thermocouple_type=channel.thermo_type, - # cjc_source=CJCSource.BUILT_IN, - # cjc_val="", - # cjc_channel="", - ) - NIDAQ_Devices = nidaqmx.system.System.local().devices - - print("NIDAQ devices ", NIDAQ_Devices) - print("NIDAQ devices names ", NIDAQ_Devices.device_names) - - Chassis = NIDAQ_Devices[0] - Module01 = NIDAQ_Devices[1] - Module02 = NIDAQ_Devices[2] - print("Chassis={}, Module01={}, Module02={}" .format(Chassis, Module01, Module02)) - - # Test resources + """Main section used during development tests""" + main_file = True + if main_file: + main(__file__) + else: try: - Chassis.self_test_device() - Module01.self_test_device() - Module02.self_test_device() - except Exception as e: - print("Resources test failed: {}" .format(e)) + print("In main") + import nidaqmx + + logger.info("DAQ Sources names{}".format(DAQ_NIDAQ_source.names())) + logger.info("DAQ Sources members{}".format(DAQ_NIDAQ_source.members())) + logger.info("DAQ Sources names0{}".format(DAQ_NIDAQ_source.names()[0])) + logger.info("DAQ Sources members0{}".format(DAQ_NIDAQ_source.members()[0])) + channels = [AIThermoChannel(name="cDAQ1Mod1/ai0", + source='Analog_Input', + analog_type='Thermocouple', + value_min=-80.0e-3, + value_max=80.0e-3, + termination='Diff', + thermo_type=DAQ_thermocouples.K), + ] + # Create Task + print("DAQ_thermocouples.names ", DAQ_thermocouples.names()) + print("DAQ_thermocouples.K ", nidaqmx.constants.ThermocoupleType.K) + print("DAQ_thermocouples.K type", type(nidaqmx.constants.ThermocoupleType.K)) + print("DAQ_thermocouples.K ", DAQ_thermocouples.K) + print("DAQ_thermocouples.K type ", type(DAQ_thermocouples.K)) + print("channel.thermo_type ", channels[0].thermo_type) + task = nidaqmx.Task() + for channel in channels: + task.ai_channels.add_ai_thrmcpl_chan(physical_channel=channel.name, + # name_to_assign_to_channel="Channel 01", + min_val=channel.value_min, + max_val=channel.value_max, + units=TemperatureUnits.DEG_C, + thermocouple_type=channel.thermo_type, + # cjc_source=CJCSource.BUILT_IN, + # cjc_val="", + # cjc_channel="", + ) + NIDAQ_Devices = nidaqmx.system.System.local().devices + + print("NIDAQ devices ", NIDAQ_Devices) + print("NIDAQ devices names ", NIDAQ_Devices.device_names) + + Chassis = NIDAQ_Devices[0] + Module01 = NIDAQ_Devices[1] + Module02 = NIDAQ_Devices[2] + print("Chassis={}, Module01={}, Module02={}" .format(Chassis, Module01, Module02)) + + # Test resources + try: + Chassis.self_test_device() + Module01.self_test_device() + Module02.self_test_device() + except Exception as e: + print("Resources test failed: {}" .format(e)) + + print("Chassis: name={}, Num={}".format(Chassis.name, Chassis.product_type)) + print("Module01: name={}, Num={}".format(Module01.name, Module01.product_type)) + print("Module02: name={}, Num={}".format(Module02.name, Module02.product_type)) + + print("channel 01 name : ", channels[0].name) + data = task.read() + print("data = ", data) + print("type(data) = ", type(data)) + print("type(data[0]) = ", type(data[0])) + + task.close() - print("Chassis: name={}, Num={}".format(Chassis.name, Chassis.product_type)) - print("Module01: name={}, Num={}".format(Module01.name, Module01.product_type)) - print("Module02: name={}, Num={}".format(Module02.name, Module02.product_type)) - - print("channel 01 name : ", channels[0].name) - data = task.read() - print("data = ", data) - print("type(data) = ", type(data)) - print("type(data[0]) = ", type(data[0])) - - task.close() - - except Exception as e: - print("Exception ({}): {}".format(type(e), str(e))) \ No newline at end of file + except Exception as e: + print("Exception ({}): {}".format(type(e), str(e))) \ No newline at end of file diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index 3522de3..186f54a 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -1,6 +1,5 @@ import time - - +import traceback from enum import IntEnum import numpy as np from pymodaq.utils.logger import set_logger, get_module_name @@ -35,6 +34,10 @@ class DAQ_NIDAQ_source(IntEnum): def names(cls): return [name for name, member in cls.__members__.items()] + @classmethod + def members(cls): + return [member for name, member in cls.__members__.items()] + class DAQ_analog_types(IntEnum): """ @@ -52,6 +55,10 @@ class DAQ_analog_types(IntEnum): def names(cls): return [name for name, member in cls.__members__.items()] + @classmethod + def members(cls): + return [member for name, member in cls.__members__.items()] + @classmethod def values(cls): return [cls[name].value for name, member in cls.__members__.items()] @@ -78,10 +85,14 @@ class DAQ_thermocouples(IntEnum): def names(cls): return [name for name, member in cls.__members__.items()] + @classmethod + def members(cls): + return [member for name, member in cls.__members__.items()] + class DAQ_termination(IntEnum): """ - Enum class of thermocouples type + Enum class of termination type =============== ========== **Attributes** **Type** @@ -97,6 +108,10 @@ class DAQ_termination(IntEnum): def names(cls): return [name for name, member in cls.__members__.items()] + @classmethod + def members(cls): + return [member for name, member in cls.__members__.items()] + class Edge(IntEnum): """ @@ -108,6 +123,10 @@ class Edge(IntEnum): def names(cls): return [name for name, member in cls.__members__.items()] + @classmethod + def members(cls): + return [member for name, member in cls.__members__.items()] + class ClockMode(IntEnum): """ @@ -119,6 +138,10 @@ class ClockMode(IntEnum): def names(cls): return [name for name, member in cls.__members__.items()] + @classmethod + def members(cls): + return [member for name, member in cls.__members__.items()] + class ClockSettingsBase: def __init__(self, Nsamples=1000, repetition=False): @@ -128,10 +151,10 @@ def __init__(self, Nsamples=1000, repetition=False): class ClockSettings(ClockSettingsBase): - def __init__(self, source=None, frequency=1000, Nsamples=1000, edge=Edge.names()[0], repetition=False): + def __init__(self, source=None, frequency=1000, Nsamples=1000, edge=Edge.members()[0], repetition=False): super().__init__(Nsamples, repetition) self.source = source - assert edge in Edge.names() + assert edge in Edge.members() self.frequency = frequency self.edge = edge @@ -145,8 +168,8 @@ def __init__(self, Nsamples=1000, rising_channel='', falling_channel='', class TriggerSettings: - def __init__(self, trig_source='', enable=False, edge=Edge.names()[0], level=0.1): - assert edge in Edge.names() + def __init__(self, trig_source='', enable=False, edge=Edge.members()[0], level=0.1): + assert edge in Edge.members() self.trig_source = trig_source self.enable = enable self.edge = edge @@ -180,16 +203,22 @@ def __init__(self, analog_type=DAQ_analog_types.names()[0], value_min=-10., valu class AIChannel(AChannel): - def __init__(self, termination=DAQ_termination.names()[0], **kwargs): + def __init__(self, termination=DAQ_termination.members(), **kwargs): super().__init__(**kwargs) - assert termination in DAQ_termination.names() + logger.info("AI chan ter{}".format(termination)) + logger.info("AI chan ter{}".format(DAQ_termination.members())) + logger.info("AI chan ter{}".format(DAQ_termination.Diff)) + # assert termination in DAQ_termination.members() self.termination = termination class AIThermoChannel(AIChannel): - def __init__(self, thermo_type=DAQ_thermocouples.names()[0], **kwargs): + def __init__(self, thermo_type=DAQ_thermocouples.members(), **kwargs): super().__init__(**kwargs) - # assert thermo_type in DAQ_thermocouples.names() + logger.info("AIthermo chan th{}".format(thermo_type)) + logger.info("AIthermo th{}".format(DAQ_thermocouples.members())) + logger.info("AIthermo th{}".format(DAQ_thermocouples.K)) + assert thermo_type in DAQ_thermocouples.members() self.thermo_type = thermo_type @@ -199,8 +228,8 @@ def __init__(self, **kwargs): class Counter(Channel): - def __init__(self, edge=Edge.names()[0], **kwargs): - assert edge in Edge.names() + def __init__(self, edge=Edge.members()[0], **kwargs): + assert edge in Edge.members() super().__init__(**kwargs) self.edge = edge self.counter_type = "Edge Counter" @@ -371,17 +400,26 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti self._task = nidaqmx.Task() err_code = None + # logger.info("first {}".format(self._task.timing)) + # logger.info("first stamp {}".format(nidaqmx.task.Timing.first_samp_timestamp_val)) # create all channels one task for one type of channels for channel in channels: + logger.info("channel {}".format(channel)) + logger.info("channel name {}".format(channel.name)) + logger.info("channel source {}".format(channel.source)) + logger.info("channel term{}".format(channel.termination)) if channel.source == 'Analog_Input': # analog input try: + logger.info("DAQ te{}".format(DAQ_termination.Diff)) if channel.analog_type == "Voltage": - self._task.ai_channels.add_ai_voltage_chan(channel.name, "", - DAQ_termination[channel.termination], - channel.value_min, - channel.value_max, - VoltageUnits.VOLTS, None) + self._task.ai_channels.add_ai_voltage_chan(physical_channel=channel.name, + name_to_assign_to_channel="", + terminal_config=channel.termination, + min_val=channel.value_min, + max_val=channel.value_max, + units=VoltageUnits.VOLTS, + custom_scale_name=None) elif channel.analog_type == "Current": self._task.ai_channels.add_ai_current_chan(channel.name, "", @@ -393,12 +431,16 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti 0., None) elif channel.analog_type == "Thermocouple": - self._task.ai_channels.add_ai_thrmcpl_chan(channel.name, "", - channel.value_min, - channel.value_max, - TemperatureUnits.DEG_C, - DAQ_termination[channel.thermo_type], - CJCSource.BUILT_IN, 0., "") + logger.info("DAQ th{}".format(DAQ_thermocouples.K)) + self._task.ai_channels.add_ai_thrmcpl_chan(physical_channel=channel.name, + name_to_assign_to_channel="", + min_val=channel.value_min, + max_val=channel.value_max, + units=TemperatureUnits.DEG_C, + thermocouple_type=channel.thermo_type, + cjc_source=CJCSource.BUILT_IN, + cjc_val=0., + cjc_channel="") except DaqError as e: err_code = e.error_code if not not err_code: @@ -477,16 +519,20 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti mode = AcquisitionType.CONTINUOUS else: mode = AcquisitionType.FINITE - if clock_settings.Nsamples > 1 and err_code == 0: + if clock_settings.Nsamples > 1 and isinstance(err_code, type(None)): try: if isinstance(clock_settings, ClockSettings): - self._task.timing.cfg_samp_clk_timing(clock_settings.source, clock_settings.frequency, - Edge[clock_settings.edge].value, - mode, - clock_settings.Nsamples) - + logger.info('------ mode {}'.format(mode)) + logger.info('------ Nsamples {}'.format(clock_settings.Nsamples)) + self._task.timing.cfg_samp_clk_timing(rate=clock_settings.frequency, + source=clock_settings.source, + active_edge=clock_settings.edge, + sample_mode=mode, + samps_per_chan=clock_settings.Nsamples) elif isinstance(clock_settings, ChangeDetectionSettings): + logger.info("here") self._task.timing.cfg_change_detection_timing(clock_settings.rising_channel, + clock_settings.falling_channel, mode, clock_settings.Nsamples) @@ -501,9 +547,10 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti if channel.source == 'Counter': pass # Maybe here adding the configuration fastCTr0 with Ctr1 etc...? else: - err = self._task.triggers.start_trigger.disable_start_trig() - if err != 0: - raise IOError(self.DAQmxGetErrorString(err)) + pass + # err = self._task.triggers.start_trigger.disable_start_trig() + # if err != 0: + # raise IOError(self.DAQmxGetErrorString(err)) else: if 'PF' in trigger_settings.trig_source: self._task.triggers.start_trigger.disable_start_trig() @@ -516,7 +563,8 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti raise IOError('Unsupported Trigger source') except Exception as e: - print(e) + logger.error("Exception catched: {}".format(e)) + logger.error(traceback.format_exc()) def register_callback(self, callback, event='done', nsamples=1): @@ -614,7 +662,7 @@ def refresh_hardware(self): controller = DAQmx() ch_counter = Counter(name='Dev1/ctr0', source='Counter', - edge=Edge.names()[0]) + edge=Edge.members()[0]) controller.update_task([ch_counter]) controller.start() print(controller.readCounter()) From 2ebd34137006f7df128fd8189ebb9916a450bcf7 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Thu, 5 Dec 2024 16:52:40 +0100 Subject: [PATCH 10/57] Reorder & update nidaqmx constants & variables --- .../hardware/national_instruments/daqmxni.py | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index 186f54a..7e2bbd2 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -24,10 +24,10 @@ class DAQ_NIDAQ_source(IntEnum): =============== ========== """ Analog_Input = 0 - Counter = 1 - Analog_Output = 2 - Digital_Output = 3 - Digital_Input = 4 + Analog_Output = 1 + Counter = 2 + Digital_Input = 3 + Digital_Output = 4 Terminals = 5 @classmethod @@ -151,7 +151,7 @@ def __init__(self, Nsamples=1000, repetition=False): class ClockSettings(ClockSettingsBase): - def __init__(self, source=None, frequency=1000, Nsamples=1000, edge=Edge.members()[0], repetition=False): + def __init__(self, source=None, frequency=1000, Nsamples=1000, edge=Edge.Rising, repetition=False): super().__init__(Nsamples, repetition) self.source = source assert edge in Edge.members() @@ -168,7 +168,7 @@ def __init__(self, Nsamples=1000, rising_channel='', falling_channel='', class TriggerSettings: - def __init__(self, trig_source='', enable=False, edge=Edge.members()[0], level=0.1): + def __init__(self, trig_source='', enable=False, edge=Edge.Rising, level=0.1): assert edge in Edge.members() self.trig_source = trig_source self.enable = enable @@ -177,7 +177,7 @@ def __init__(self, trig_source='', enable=False, edge=Edge.members()[0], level=0 class Channel: - def __init__(self, name='', source=DAQ_NIDAQ_source.names()[0]): + def __init__(self, name='', source=DAQ_NIDAQ_source.Analog_Input): """ Parameters ---------- @@ -189,7 +189,7 @@ def __init__(self, name='', source=DAQ_NIDAQ_source.names()[0]): class AChannel(Channel): - def __init__(self, analog_type=DAQ_analog_types.names()[0], value_min=-10., value_max=+10., **kwargs): + def __init__(self, analog_type=DAQ_analog_types.Voltage, value_min=-10., value_max=+10., **kwargs): """ Parameters ---------- @@ -203,21 +203,15 @@ def __init__(self, analog_type=DAQ_analog_types.names()[0], value_min=-10., valu class AIChannel(AChannel): - def __init__(self, termination=DAQ_termination.members(), **kwargs): + def __init__(self, termination=DAQ_termination.Auto, **kwargs): super().__init__(**kwargs) - logger.info("AI chan ter{}".format(termination)) - logger.info("AI chan ter{}".format(DAQ_termination.members())) - logger.info("AI chan ter{}".format(DAQ_termination.Diff)) - # assert termination in DAQ_termination.members() + assert termination in DAQ_termination.members() self.termination = termination class AIThermoChannel(AIChannel): - def __init__(self, thermo_type=DAQ_thermocouples.members(), **kwargs): + def __init__(self, thermo_type=DAQ_thermocouples.K, **kwargs): super().__init__(**kwargs) - logger.info("AIthermo chan th{}".format(thermo_type)) - logger.info("AIthermo th{}".format(DAQ_thermocouples.members())) - logger.info("AIthermo th{}".format(DAQ_thermocouples.K)) assert thermo_type in DAQ_thermocouples.members() self.thermo_type = thermo_type @@ -228,7 +222,7 @@ def __init__(self, **kwargs): class Counter(Channel): - def __init__(self, edge=Edge.members()[0], **kwargs): + def __init__(self, edge=Edge.Rising, **kwargs): assert edge in Edge.members() super().__init__(**kwargs) self.edge = edge @@ -338,10 +332,10 @@ def get_NIDAQ_channels(cls, devices=None, source_type=None): for source in source_type: if source == DAQ_NIDAQ_source['Analog_Input'].name: # analog input channels = Device(device).ai_physical_chans.channel_names - elif source == DAQ_NIDAQ_source['Counter'].name: # counter - channels = Device(device).ci_physical_chans.channel_names elif source == DAQ_NIDAQ_source['Analog_Output'].name: # analog output channels = Device(device).ao_physical_chans.channel_names + elif source == DAQ_NIDAQ_source['Counter'].name: # counter + channels = Device(device).ci_physical_chans.channel_names elif source == DAQ_NIDAQ_source['Digital_Output'].name: # digital output channels = Device(device).do_lines.channel_names elif source == DAQ_NIDAQ_source['Digital_Input'].name: # digital iutput @@ -450,7 +444,7 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti try: if channel.counter_type == "Edge Counter": self._task.ci_channels.add_ci_count_edges_chan(channel.name, "", - Edge[channel.edge], 0, + channel.edge, 0, CountDirection.COUNT_UP) elif channel.counter_type == "Clock Output": From 9193b7e22948d7145071489132b3fcbcdfbee7b1 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Thu, 5 Dec 2024 16:56:44 +0100 Subject: [PATCH 11/57] Clarify code --- .../hardware/national_instruments/daqmxni.py | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index 7e2bbd2..0bc0034 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -407,34 +407,35 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti try: logger.info("DAQ te{}".format(DAQ_termination.Diff)) if channel.analog_type == "Voltage": - self._task.ai_channels.add_ai_voltage_chan(physical_channel=channel.name, - name_to_assign_to_channel="", - terminal_config=channel.termination, - min_val=channel.value_min, - max_val=channel.value_max, - units=VoltageUnits.VOLTS, - custom_scale_name=None) + self._task.ai_channels.add_ai_voltage_chan(channel.name, + "", + channel.termination, + channel.value_min, + channel.value_max, + VoltageUnits.VOLTS, + "") elif channel.analog_type == "Current": - self._task.ai_channels.add_ai_current_chan(channel.name, "", - DAQ_termination[channel.termination], + self._task.ai_channels.add_ai_current_chan(channel.name, + "", + channel.termination, channel.value_min, channel.value_max, CurrentUnits.AMPS, CurrentShuntResistorLocation.INTERNAL, - 0., None) + 0., + "") elif channel.analog_type == "Thermocouple": - logger.info("DAQ th{}".format(DAQ_thermocouples.K)) - self._task.ai_channels.add_ai_thrmcpl_chan(physical_channel=channel.name, - name_to_assign_to_channel="", - min_val=channel.value_min, - max_val=channel.value_max, - units=TemperatureUnits.DEG_C, - thermocouple_type=channel.thermo_type, - cjc_source=CJCSource.BUILT_IN, - cjc_val=0., - cjc_channel="") + self._task.ai_channels.add_ai_thrmcpl_chan(channel.name, + "", + channel.value_min, + channel.value_max, + TemperatureUnits.DEG_C, + channel.thermo_type, + CJCSource.BUILT_IN, + 0., + "") except DaqError as e: err_code = e.error_code if not not err_code: @@ -563,14 +564,13 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti def register_callback(self, callback, event='done', nsamples=1): if event == 'done': - self._task.register_done_event(Signal.SAMPLE_COMPLETE, callback) + self._task.register_done_event(callback) # NOT SURE HERE elif event == 'sample': - self._task.register_every_n_samples_acquired_into_buffer_event(1, - callback) + self._task.register_every_n_samples_acquired_into_buffer_event(1, callback) elif event == 'Nsamples': - self._task.register_every_n_samples_acquired_into_buffer_event(nsamples, - callback) + self._task.register_every_n_samples_acquired_into_buffer_event(nsamples, callback) + def readAnalog(self, Nchannels, clock_settings): read = nidaqmx.int32() N = clock_settings.Nsamples From 36319084b46c578fb85f3757563a7744b055e46b Mon Sep 17 00:00:00 2001 From: Sebastien Date: Thu, 5 Dec 2024 16:57:48 +0100 Subject: [PATCH 12/57] Remove old PyDAQMX based-on method --- .../hardware/national_instruments/daqmxni.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index 0bc0034..4bbc25c 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -571,19 +571,6 @@ def register_callback(self, callback, event='done', nsamples=1): elif event == 'Nsamples': self._task.register_every_n_samples_acquired_into_buffer_event(nsamples, callback) - def readAnalog(self, Nchannels, clock_settings): - read = nidaqmx.int32() - N = clock_settings.Nsamples - data = np.zeros(N * Nchannels, dtype=np.float64) - timeout = N * Nchannels * 1 / clock_settings.frequency * 2 # set to twice the time it should take to acquire the data - - self._task.ReadAnalogF64(N, timeout, nidaqmx.DAQmx_Val_GroupByChannel, data, len(data), - nidaqmx.byref(read), None) - if read.value == N: - return data - else: - raise IOError(f'Insufficient number of samples have been read:{read.value}/{N}') - def readCounter(self): # return 25 From a1989ae6ce3a96f2a2d1527bdd17395b3b6f4754 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Thu, 5 Dec 2024 17:02:44 +0100 Subject: [PATCH 13/57] devices attributes not a list of string anymore but a list of nidaqmx.devices objects --- .../plugins_0D/daq_0Dviewer_NIDAQmx.py | 10 ++-------- .../hardware/national_instruments/daqmxni.py | 16 +++++++++------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py index 670bf9c..3d3b1cf 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py @@ -18,14 +18,8 @@ class DAQ_0DViewer_NIDAQmx(DAQ_Viewer_base): params = comon_parameters+[ {'title': 'Display type:', 'name': 'display', 'type': 'list', 'limits': ['0D', '1D']}, - {'title': 'Module ref. :', 'name': 'module', 'type': 'list', 'limits': DAQmx.get_NIDAQ_devices(), - 'value': DAQmx.get_NIDAQ_devices()[0] - }, - {'title': 'Devices', 'name': 'devices', 'type': 'text', 'value': ''}, - {'title': 'Channels', 'name': 'channels', 'type': 'text', 'value': ''}, - {'title': 'Analog channel :', 'name': 'ai_channel', 'type': 'list', - 'limits': DAQmx.get_NIDAQ_channels(source_type='Analog_Input'), - 'value': DAQmx.get_NIDAQ_channels(source_type='Analog_Input')[0] + {'title': 'Module ref. :', 'name': 'module', 'type': 'list', 'limits': DAQmx.get_NIDAQ_devices()[1], + 'value': DAQmx.get_NIDAQ_devices()[1][0] }, {'title': 'Analog Source :', 'name': 'ai_srce', 'type': 'list', 'limits': DAQ_NIDAQ_source.names(), diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index 4bbc25c..e2e8548 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -255,6 +255,7 @@ class DAQmx: """Wrapper around the NIDAQmx package giving an easy to use object to instantiate channels and tasks""" def __init__(self): self.devices = [] + self.devices_names = [] self.channels = [] self._device = None self._task = None @@ -275,12 +276,13 @@ def device(self): @device.setter def device(self, device): - if device not in self.devices: + if device not in self.devices_names: raise IOError(f'your device: {device} is not known or connected') self._device = device def update_NIDAQ_devices(self): - self.devices = self.get_NIDAQ_devices() + self.devices = self.get_NIDAQ_devices()[0] + self.devices_names = self.get_NIDAQ_devices()[1] @classmethod def get_NIDAQ_devices(cls): @@ -292,15 +294,15 @@ def get_NIDAQ_devices(cls): list of devices as strings to be used in subsequent commands """ try: - devices = nidaqmx.system.System.local().devices.device_names + devices = nidaqmx.system.System.local().devices if devices == ['']: devices = [] - return devices + return devices, devices.device_names except DaqError as e: return e.error_code def update_NIDAQ_channels(self, source_type=None): - self.channels = self.get_NIDAQ_channels(self.devices, source_type=source_type) + self.channels = self.get_NIDAQ_channels(self.devices_names, source_type=source_type) @classmethod def get_NIDAQ_channels(cls, devices=None, source_type=None): @@ -319,7 +321,7 @@ def get_NIDAQ_channels(cls, devices=None, source_type=None): """ if devices is None: - devices = cls.get_NIDAQ_devices() + devices = cls.get_NIDAQ_devices()[1] if source_type is None: source_type = DAQ_NIDAQ_source.names() @@ -368,7 +370,7 @@ def isDigitalTriggeringSupported(cls, device): def getTriggeringSources(cls, devices=None): sources = [] if devices is None: - devices = cls.get_NIDAQ_devices() + devices = cls.get_NIDAQ_devices()[1] for device in devices: if cls.isDigitalTriggeringSupported(device): From 4f7bf5d2b1678894eda2d1c5210ea412af7e4437 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Thu, 5 Dec 2024 17:11:48 +0100 Subject: [PATCH 14/57] Clarify code --- .../hardware/national_instruments/daqmxni.py | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index e2e8548..806c84a 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -395,19 +395,13 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti self.c_callback = None self._task = nidaqmx.Task() + logger.info("TASK: {}".format(self._task)) err_code = None - # logger.info("first {}".format(self._task.timing)) - # logger.info("first stamp {}".format(nidaqmx.task.Timing.first_samp_timestamp_val)) # create all channels one task for one type of channels for channel in channels: - logger.info("channel {}".format(channel)) - logger.info("channel name {}".format(channel.name)) - logger.info("channel source {}".format(channel.source)) - logger.info("channel term{}".format(channel.termination)) if channel.source == 'Analog_Input': # analog input try: - logger.info("DAQ te{}".format(DAQ_termination.Diff)) if channel.analog_type == "Voltage": self._task.ai_channels.add_ai_voltage_chan(channel.name, "", @@ -519,17 +513,13 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti if clock_settings.Nsamples > 1 and isinstance(err_code, type(None)): try: if isinstance(clock_settings, ClockSettings): - logger.info('------ mode {}'.format(mode)) - logger.info('------ Nsamples {}'.format(clock_settings.Nsamples)) - self._task.timing.cfg_samp_clk_timing(rate=clock_settings.frequency, - source=clock_settings.source, - active_edge=clock_settings.edge, - sample_mode=mode, - samps_per_chan=clock_settings.Nsamples) + self._task.timing.cfg_samp_clk_timing(clock_settings.frequency, + clock_settings.source, + clock_settings.edge, + mode, + clock_settings.Nsamples) elif isinstance(clock_settings, ChangeDetectionSettings): - logger.info("here") self._task.timing.cfg_change_detection_timing(clock_settings.rising_channel, - clock_settings.falling_channel, mode, clock_settings.Nsamples) @@ -537,6 +527,7 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti err_code = e.error_code if not not err_code: status = self.DAQmxGetErrorString(err_code) + logger.error(traceback.format_exc()) raise IOError(status) for channel in channels: @@ -558,9 +549,9 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti trigger_settings.level) else: raise IOError('Unsupported Trigger source') - + logger.info("Task's channels{}".format(self._task.ai_channels.channel_names)) except Exception as e: - logger.error("Exception catched: {}".format(e)) + logger.error("Exception caught: {}".format(e)) logger.error(traceback.format_exc()) def register_callback(self, callback, event='done', nsamples=1): From daf959c71e547c4a7a014280b9cf7a3ab5253f4b Mon Sep 17 00:00:00 2001 From: Sebastien Date: Thu, 5 Dec 2024 17:20:22 +0100 Subject: [PATCH 15/57] PEP + syntax --- .../national_instruments/daq_NIDAQmx.py | 143 ++++++++++-------- 1 file changed, 76 insertions(+), 67 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index f0f28b8..5b6e9d9 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -74,6 +74,8 @@ def addNew(self, typ): 'removable': True, 'renamable': False} self.addChild(child) + + registerParameterType('groupai', ScalableGroupAI, override=True) @@ -127,6 +129,8 @@ def addNew(self, typ): 'removable': True, 'renamable': False} self.addChild(child) + + registerParameterType('groupao', ScalableGroupAO, override=True) @@ -171,6 +175,8 @@ def addNew(self, typ): 'removable': True, 'renamable': False} self.addChild(child) + + registerParameterType('groupcounter', ScalableGroupCounter, override=True) @@ -204,6 +210,8 @@ def addNew(self, typ): child = {'title': typ, 'name': 'di{:02.0f}'.format(newindex), 'type': 'group', 'children': self.params, 'removable': True, 'renamable': False} self.addChild(child) + + registerParameterType('groupdi', ScalableGroupDI, override=True) @@ -237,54 +245,59 @@ def addNew(self, typ): child = {'title': typ, 'name': 'counter{:02.0f}'.format(newindex), 'type': 'group', 'children': self.params, 'removable': True, 'renamable': False} self.addChild(child) + + registerParameterType('groupdo', ScalableGroupDO, override=True) class DAQ_NIDAQmx_base(DAQmx): data_grabed_signal = Signal(list) - params =[{'title': 'Refresh hardware:', 'name': 'refresh_hardware', 'type': 'bool', 'value': False}, - {'title': 'Signal type:', 'name': 'NIDAQ_type', 'type': 'list', 'limits': DAQ_NIDAQ_source.names()}, - {'title': 'AO Settings:', 'name': 'ao_settings', 'type': 'group', 'children': [ - {'title': 'Waveform:', 'name': 'waveform', 'type': 'list', 'value': 'DC', 'limits': ['DC', 'Sinus', 'Ramp']}, - - {'title': 'Controlled param:', 'name': 'cont_param', 'type': 'list', 'value': 'offset', - 'limits': ['offset', 'amplitude', 'frequency']}, - {'title': 'Waveform Settings:', 'name': 'waveform_settings', 'type': 'group', 'visible': False, 'children': [ - {'title': 'Offset:', 'name': 'offset', 'type': 'float', 'value': 0., }, - {'title': 'Amplitude:', 'name': 'amplitude', 'type': 'float', 'value': 1., }, - {'title': 'Frequency:', 'name': 'frequency', 'type': 'float', 'value': 10., }, - ]}, - ]}, - {'title': 'Clock Settings:', 'name': 'clock_settings', 'type': 'group', 'children': [ - {'title': 'Nsamples:', 'name': 'Nsamples', 'type': 'int', 'value': 1000, 'default': 1000, 'min': 1}, - {'title': 'Frequency:', 'name': 'frequency', 'type': 'float', 'value': 1000., 'default': 1000., - 'min': 0., 'suffix': 'Hz'}, - {'title': 'Repetition?:', 'name': 'repetition', 'type': 'bool', 'value': False, }, - ] - }, - {'title': 'AI Channels:', 'name': 'ai_channels', 'type': 'groupai', - 'limits': DAQmx.get_NIDAQ_channels(source_type='Analog_Input')}, - {'title': 'AO Channels:', 'name': 'ao_channels', 'type': 'groupao', - 'limits': DAQmx.get_NIDAQ_channels(source_type='Analog_Output')}, - {'title': 'DO Channels:', 'name': 'do_channels', 'type': 'groupdo', - 'limits': DAQmx.get_NIDAQ_channels(source_type='Digital_Output')}, - {'title': 'DI Channels:', 'name': 'di_channels', 'type': 'groupdi', - 'limits': DAQmx.get_NIDAQ_channels(source_type='Digital_Input')}, - {'title': 'Counter Settings:', 'name': 'counter_settings', 'type': 'group', 'visible': True, 'children': [ - {'title': 'Counting time (ms):', 'name': 'counting_time', 'type': 'float', 'value': 100., - 'default': 100., 'min': 0.}, - {'title': 'Counting Channels:', 'name': 'counter_channels', 'type': 'groupcounter', - 'limits': DAQmx.get_NIDAQ_channels(source_type='Counter')}, - ]}, - {'title': 'Trigger Settings:', 'name': 'trigger_settings', 'type': 'group', 'visible': True, 'children': [ - {'title': 'Enable?:', 'name': 'enable', 'type': 'bool', 'value': False, }, - {'title': 'Trigger Source:', 'name': 'trigger_channel', 'type': 'list', - 'limits': DAQmx.getTriggeringSources()}, - {'title': 'Edge type:', 'name': 'edge', 'type': 'list', 'limits': Edge.names(), 'visible': False}, - {'title': 'Level:', 'name': 'level', 'type': 'float', 'value': 1., 'visible': False} - ]} - ] + params = [{'title': 'Refresh hardware:', 'name': 'refresh_hardware', 'type': 'bool', 'value': False}, + {'title': 'Signal type:', 'name': 'NIDAQ_type', 'type': 'list', 'limits': DAQ_NIDAQ_source.names()}, + {'title': 'NSamples To Read', 'name': 'nsamplestoread', 'type': 'int', 'value': 1000, 'default': 1000, + 'min': 1}, + {'title': 'AO Settings:', 'name': 'ao_settings', 'type': 'group', 'children': [ + {'title': 'Waveform:', 'name': 'waveform', 'type': 'list', 'value': 'DC', + 'limits': ['DC', 'Sinus', 'Ramp']}, + {'title': 'Controlled param:', 'name': 'cont_param', 'type': 'list', 'value': 'offset', + 'limits': ['offset', 'amplitude', 'frequency']}, + {'title': 'Waveform Settings:', 'name': 'waveform_settings', 'type': 'group', 'visible': False, + 'children': [ + {'title': 'Offset:', 'name': 'offset', 'type': 'float', 'value': 0., }, + {'title': 'Amplitude:', 'name': 'amplitude', 'type': 'float', 'value': 1., }, + {'title': 'Frequency:', 'name': 'frequency', 'type': 'float', 'value': 10., }, + ]}, + ]}, + {'title': 'Clock Settings:', 'name': 'clock_settings', 'type': 'group', 'children': [ + {'title': 'Nsamples:', 'name': 'Nsamples', 'type': 'int', 'value': 1000, 'default': 1000, 'min': 1}, + {'title': 'Frequency:', 'name': 'frequency', 'type': 'float', 'value': 1000., 'default': 1000., + 'min': 0., 'suffix': 'Hz'}, + {'title': 'Repetition?:', 'name': 'repetition', 'type': 'bool', 'value': False, }, + ] + }, + {'title': 'AI Channels:', 'name': 'ai_channels', 'type': 'groupai', + 'limits': DAQmx.get_NIDAQ_channels(source_type='Analog_Input')}, + {'title': 'AO Channels:', 'name': 'ao_channels', 'type': 'groupao', + 'limits': DAQmx.get_NIDAQ_channels(source_type='Analog_Output')}, + {'title': 'DO Channels:', 'name': 'do_channels', 'type': 'groupdo', + 'limits': DAQmx.get_NIDAQ_channels(source_type='Digital_Output')}, + {'title': 'DI Channels:', 'name': 'di_channels', 'type': 'groupdi', + 'limits': DAQmx.get_NIDAQ_channels(source_type='Digital_Input')}, + {'title': 'Counter Settings:', 'name': 'counter_settings', 'type': 'group', 'visible': True, 'children': [ + {'title': 'Counting time (ms):', 'name': 'counting_time', 'type': 'float', 'value': 100., + 'default': 100., 'min': 0.}, + {'title': 'Counting Channels:', 'name': 'counter_channels', 'type': 'groupcounter', + 'limits': DAQmx.get_NIDAQ_channels(source_type='Counter')}, + ]}, + {'title': 'Trigger Settings:', 'name': 'trigger_settings', 'type': 'group', 'visible': True, 'children': [ + {'title': 'Enable?:', 'name': 'enable', 'type': 'bool', 'value': False, }, + {'title': 'Trigger Source:', 'name': 'trigger_channel', 'type': 'list', + 'limits': DAQmx.getTriggeringSources()}, + {'title': 'Edge type:', 'name': 'edge', 'type': 'list', 'limits': Edge.names(), 'visible': False}, + {'title': 'Level:', 'name': 'level', 'type': 'float', 'value': 1., 'visible': False} + ]} + ] def __init__(self): super().__init__() @@ -314,8 +327,8 @@ def commit_settings(self, param: Parameter): # self.update_task() if param.name() == 'NIDAQ_type': - self.update_NIDAQ_channels(param.value()) - if param.value() == DAQ_NIDAQ_source(0).name: #analog input + self.controller.update_NIDAQ_channels(param.value()) + if param.value() == DAQ_NIDAQ_source(0).name: # analog input self.settings.child('clock_settings').show() self.settings.child('ai_channels').show() self.settings.child('ao_channels').hide() @@ -324,7 +337,7 @@ def commit_settings(self, param: Parameter): self.settings.child('do_channels').hide() self.settings.child('di_channels').hide() - elif param.value() == DAQ_NIDAQ_source(1).name: #counter input + elif param.value() == DAQ_NIDAQ_source(1).name: # counter input self.settings.child('clock_settings').hide() self.settings.child('ai_channels').hide() self.settings.child('ao_channels').hide() @@ -333,7 +346,7 @@ def commit_settings(self, param: Parameter): self.settings.child('do_channels').hide() self.settings.child('di_channels').hide() - elif param.value() == DAQ_NIDAQ_source(2).name: #analog output + elif param.value() == DAQ_NIDAQ_source(2).name: # analog output self.settings.child('clock_settings').show() self.settings.child('ai_channels').hide() self.settings.child('ao_channels').show() @@ -342,7 +355,7 @@ def commit_settings(self, param: Parameter): self.settings.child('do_channels').hide() self.settings.child('di_channels').hide() - elif param.value() == DAQ_NIDAQ_source(3).name: # digital output + elif param.value() == DAQ_NIDAQ_source(3).name: # digital output self.settings.child('clock_settings').show() self.settings.child('ai_channels').hide() self.settings.child('ao_channels').hide() @@ -351,7 +364,7 @@ def commit_settings(self, param: Parameter): self.settings.child('do_channels').show() self.settings.child('di_channels').hide() - elif param.value() == DAQ_NIDAQ_source(4).name: # Digital_Input + elif param.value() == DAQ_NIDAQ_source(4).name: # Digital_Input self.settings.child('clock_settings').show() self.settings.child('ai_channels').hide() self.settings.child('ao_channels').show() @@ -634,18 +647,17 @@ class DAQ_NIDAQmx_Actuator(DAQ_Move_base, DAQ_NIDAQmx_base): is_multiaxes = False # set to True if this plugin is controlled for a multiaxis controller (with a unique communication link) stage_names = [] # "list of strings of the multiaxes - params = DAQ_NIDAQmx_base.params +[ - # elements to be added here as dicts in order to control your custom stage - ############ - {'title': 'MultiAxes:', 'name': 'multiaxes', 'type': 'group', 'visible': is_multiaxes, - 'children': [ - {'title': 'is Multiaxes:', 'name': 'ismultiaxes', 'type': 'bool', 'value': is_multiaxes, - 'default': False}, - {'title': 'Status:', 'name': 'multi_status', 'type': 'list', 'value': 'Master', - 'limits': ['Master', 'Slave']}, - {'title': 'Axis:', 'name': 'axis', 'type': 'list', 'limits': stage_names}, - - ]}] + actuator_params + params = DAQ_NIDAQmx_base.params + [ + # elements to be added here as dicts in order to control your custom stage + ############ + {'title': 'MultiAxes:', 'name': 'multiaxes', 'type': 'group', 'visible': is_multiaxes, + 'children': [ + {'title': 'is Multiaxes:', 'name': 'ismultiaxes', 'type': 'bool', 'value': is_multiaxes, + 'default': False}, + {'title': 'Status:', 'name': 'multi_status', 'type': 'list', 'value': 'Master', + 'limits': ['Master', 'Slave']}, + {'title': 'Axis:', 'name': 'axis', 'type': 'list', 'limits': stage_names}, + ]}] + actuator_params def __init__(self, parent=None, params_state=None, control_type="Actuator"): DAQ_Move_base.__init__(self, parent, params_state) # defines settings attribute and various other methods @@ -671,7 +683,6 @@ def get_actuator_value(self) -> DataActuator: self.emit_status(ThreadCommand('check_position', [pos])) return pos - def commit_settings(self, param): """ | Activate any parameter changes on the PI_GCS2 hardware. @@ -721,7 +732,7 @@ def ini_stage(self, controller=None): # check whether this stage is controlled by a multiaxe controller (to be defined for each plugin) # if multiaxes then init the controller here if Master state otherwise use external controller if self.settings['multiaxes', 'ismultiaxes'] and self.settings['multiaxes', - 'multi_status'] == "Slave": + 'multi_status'] == "Slave": if controller is None: raise Exception('no controller has been defined externally while this axe is a slave one') else: @@ -757,13 +768,12 @@ def calulate_waveform(self, value): amp = self.settings['ao_settings', 'waveform_settings', 'amplitude'] offset = self.settings['ao_settings', 'waveform_settings', 'offset'] if waveform == 'Sinus': - values = offset + amp * np.sin(2*np.pi*freq0*time) + values = offset + amp * np.sin(2 * np.pi * freq0 * time) elif waveform == 'Ramp': values = offset + amp * np.linspace(0, 1, Nsamples) return values - def move_Abs(self, position): """ Move the actuator to the absolute target defined by position @@ -776,7 +786,7 @@ def move_Abs(self, position): position = self.set_position_with_scaling(position) # apply scaling if the user specified one if self.settings['NIDAQ_type'] == 'Analog_Output': self.settings.child('ao_settings', 'waveform_settings', - self.settings['ao_settings', 'cont_param']).setValue(position) + self.settings['ao_settings', 'cont_param']).setValue(position) values = self.calulate_waveform(position) self.target_position = position @@ -813,7 +823,7 @@ def move_Rel(self, position): self.target_position = position + self.current_position if self.settings['NIDAQ_type'] == 'Analog_Output': self.settings.child('ao_settings', 'waveform_settings', - self.settings['ao_settings', 'cont_param']).setValue(self.target_position) + self.settings['ao_settings', 'cont_param']).setValue(self.target_position) values = self.calulate_waveform(self.target_position) @@ -860,4 +870,3 @@ def stop_motion(self): self.emit_status(ThreadCommand('Update_Status', ['Some info you want to log'])) self.move_done() # to let the interface know the actuator stopped ############################## - From 7d44cc55902245213c45c12056921f66c6c6f3cd Mon Sep 17 00:00:00 2001 From: Sebastien Date: Thu, 5 Dec 2024 17:22:08 +0100 Subject: [PATCH 16/57] nidaqmx.task parameters --- .../national_instruments/daq_NIDAQmx.py | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index 5b6e9d9..583013d 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -401,13 +401,13 @@ def update_task(self): self.channels = self.get_channels_from_settings() self.clock_settings = ClockSettings(frequency=self.settings['clock_settings', 'frequency'], Nsamples=self.settings['clock_settings', 'Nsamples'], - repetition=self.live,) + edge=Edge.Rising, + repetition=self.live, ) self.trigger_settings = \ TriggerSettings(trig_source=self.settings['trigger_settings', 'trigger_channel'], enable=self.settings['trigger_settings', 'enable'], - edge=self.settings['trigger_settings', 'edge'], - level=self.settings['trigger_settings', 'level'],) - + edge=Edge[self.settings['trigger_settings', 'edge']], + level=self.settings['trigger_settings', 'level'], ) if not not self.channels: super().update_task(self.channels, self.clock_settings, trigger_settings=self.trigger_settings) @@ -422,20 +422,21 @@ def get_channels_from_settings(self): source='Analog_Input', analog_type=analog_type, value_min=channel['voltage_settings', 'volt_min'], value_max=channel['voltage_settings', 'volt_max'], - termination=channel['termination'],)) + termination=DAQ_termination[channel['termination']], )) elif analog_type == 'Current': channels.append(AIChannel(name=channel.opts['title'], source='Analog_Input', analog_type=analog_type, value_min=channel['current_settings', 'curr_min'], value_max=channel['current_settings', 'curr_max'], - termination=channel['termination'], )) + termination=DAQ_termination[channel['termination']], )) elif analog_type == 'Thermocouple': channels.append(AIThermoChannel(name=channel.opts['title'], - source='Analog_Input', analog_type=analog_type, - value_min=channel['thermoc_settings', 'T_min'], - value_max=channel['thermoc_settings', 'T_max'], - termination=channel['termination'], - thermo_type=channel['thermoc_settings', 'thermoc_type'],)) + source='Analog_Input', analog_type=analog_type, + value_min=channel['thermoc_settings', 'T_min'], + value_max=channel['thermoc_settings', 'T_max'], + termination=channel['termination'], + thermo_type=DAQ_thermocouples[ + channel['thermoc_settings', 'thermoc_type']], )) elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(1).name: # counter input for channel in self.settings.child('counter_settings', 'counter_channels').children(): From 31c5d6a4c0f3ac3146e551f49677a057f394331c Mon Sep 17 00:00:00 2001 From: Sebastien Date: Thu, 5 Dec 2024 17:26:58 +0100 Subject: [PATCH 17/57] PEP + syntax --- .../national_instruments/daq_NIDAQmx.py | 58 ++++++++----------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index 583013d..8641450 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -322,9 +322,9 @@ def commit_settings(self, param: Parameter): -------- update_NIDAQ_channels, update_task, DAQ_NIDAQ_source, refresh_hardware """ - # if param.name() == 'NIDAQ_devices': - # self.update_NIDAQ_channels() - # self.update_task() + if param.name() == 'NIDAQ_devices': + self.controller.update_NIDAQ_channels() + self.update_task() if param.name() == 'NIDAQ_type': self.controller.update_NIDAQ_channels(param.value()) @@ -376,7 +376,7 @@ def commit_settings(self, param: Parameter): elif param.name() == 'refresh_hardware': if param.value(): - self.refresh_hardware() + self.controller.refresh_hardware() QtWidgets.QApplication.processEvents() self.settings.child('refresh_hardware').setValue(False) @@ -409,8 +409,8 @@ def update_task(self): edge=Edge[self.settings['trigger_settings', 'edge']], level=self.settings['trigger_settings', 'level'], ) if not not self.channels: - super().update_task(self.channels, self.clock_settings, trigger_settings=self.trigger_settings) - + logger.info("not not self channel") + self.controller.update_task(self.channels, self.clock_settings, trigger_settings=self.trigger_settings) def get_channels_from_settings(self): channels = [] @@ -467,8 +467,7 @@ def stop(self): if not not self.timer: self.timer.stop() QtWidgets.QApplication.processEvents() - DAQmx.stop(self) - + self.controller.stop() class DAQ_NIDAQmx_Viewer(DAQ_Viewer_base, DAQ_NIDAQmx_base): """ @@ -488,13 +487,14 @@ class DAQ_NIDAQmx_Viewer(DAQ_Viewer_base, DAQ_NIDAQmx_base): params = viewer_params + DAQ_NIDAQmx_base.params def __init__(self, parent=None, params_state=None, control_type="0D"): - DAQ_Viewer_base.__init__(self, parent, params_state) #defines settings attribute and various other methods + DAQ_Viewer_base.__init__(self, parent, params_state) # defines settings attribute and various other methods DAQ_NIDAQmx_base.__init__(self) self.live = False self.control_type = control_type # could be "0D", "1D" or "Actuator" if self.control_type == "0D": - self.settings.child('NIDAQ_type').setLimits(['Analog_Input', 'Counter', 'Digital_Input']) # analog input and counter + self.settings.child('NIDAQ_type').setLimits( + ['Analog_Input', 'Counter', 'Digital_Input']) # analog input and counter elif self.control_type == "1D": self.settings.child('NIDAQ_type').setLimits(['Analog_Input']) elif self.control_type == "Actuator": @@ -508,12 +508,10 @@ def __init__(self, parent=None, params_state=None, control_type="0D"): self.timer.timeout.connect(self.counter_done) def stop(self): - try: - self.controller['ai'].task.StopTask() - except: - pass - ############################## - + """Stop the current grab hardware wise if necessary""" + self.controller.stop() + self.live = False + self.emit_status(ThreadCommand('Update_Status', ['Acquisition stopped.'])) return '' def commit_settings(self, param): @@ -535,7 +533,7 @@ def commit_settings(self, param): device = param.opts['title'].split('/')[0] self.settings.child('clock_settings', 'frequency').setOpts(max=self.getAIMaxRate(device)) - ranges = self.getAIVoltageRange(device) + ranges = self.controller.getAIVoltageRange(device) param.child('voltage_settings', 'volt_min').setOpts(limits=[r[0] for r in ranges]) param.child('voltage_settings', 'volt_max').setOpts(limits=[r[1] for r in ranges]) @@ -551,15 +549,10 @@ def ini_detector(self, controller=None): """ self.status.update(edict(initialized=False, info="", x_axis=None, y_axis=None, controller=None)) try: - if self.settings['controller_status'] == "Slave": - if controller is None: - raise Exception('no controller has been defined externally while this detector is a slave one') - else: - self.controller = 'A Nidaqmx task' - else: - self.update_task() + self.controller = self.ini_detector_init(controller, DAQmx()) + self.update_task() - #actions to perform in order to set properly the settings tree options + # actions to perform in order to set properly the settings tree options self.commit_settings(self.settings.child('NIDAQ_type')) self.status.info = "Plugin Initialized" @@ -589,6 +582,7 @@ def grab_data(self, Naverage=1, **kwargs): DAQ_NIDAQ_source """ update = False + if 'live' in kwargs: if kwargs['live'] != self.live: update = True @@ -596,13 +590,8 @@ def grab_data(self, Naverage=1, **kwargs): if update: self.update_task() - if self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(0).name: #analog input - if self.c_callback is None: - self.register_callback(self.emit_data) - elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(1).name: #counter input - self.timer.start(self.settings['counter_settings', 'counting_time']) - self.waitTaskDone() - self.start() + if self.controller.task is None: + self.update_task() def emit_data(self, taskhandle, status, callbackdata): channels_name = [ch.name for ch in self.channels] @@ -630,11 +619,10 @@ def counter_done(self): data_counter = self.readCounter(len(self.channels), self.settings['counter_settings', 'counting_time'] * 1e-3) self.data_grabed_signal.emit([DataFromPlugins(name='NI Counter', data=[data_counter / 1e-3], dim='Data0D', - labels=channels_name,)]) - #y_axis=Axis(label='Count Number', units='1/s'))]) + labels=channels_name, )]) + # y_axis=Axis(label='Count Number', units='1/s'))]) self.task.StopTask() - class DAQ_NIDAQmx_Actuator(DAQ_Move_base, DAQ_NIDAQmx_base): """ Wrapper object to access the Mock fonctionnalities, similar wrapper for all controllers. From 612e403179f98b3aef8b7493fda4ccf3d76ea69f Mon Sep 17 00:00:00 2001 From: Sebastien Date: Thu, 5 Dec 2024 17:30:09 +0100 Subject: [PATCH 18/57] Callback method --- .../national_instruments/daq_NIDAQmx.py | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index 8641450..558b1a9 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -593,26 +593,27 @@ def grab_data(self, Naverage=1, **kwargs): if self.controller.task is None: self.update_task() - def emit_data(self, taskhandle, status, callbackdata): - channels_name = [ch.name for ch in self.channels] + self.controller.register_callback(self.emit_data, "Nsamples", self.clock_settings.Nsamples) + self.controller.start() - data_tot = [] - data = self.readAnalog(len(self.channels), self.clock_settings) - N = self.clock_settings.Nsamples + def emit_data(self, task_handle, every_n_samples_event_type, number_of_samples, callback_data): + channels_names = [ch.name for ch in self.channels] + # channels_ai_names = [ch.name for ch in self.channels if ch.source == 'Analog_Input'] + data_from_task = self.controller.task.read(self.settings['nsamplestoread'], timeout=20.0) if self.control_type == "0D": - for ind in range(len(self.channels)): - data_tot.append(np.array([np.mean(data[ind*N:(ind+1)*N])])) - self.data_grabed_signal.emit([DataFromPlugins(name='NI AI', data=data_tot, dim='Data0D', - labels=channels_name)]) - else: - for ind in range(len(self.channels)): - data_tot.append(data[ind*N:(ind+1)*N]) - self.data_grabed_signal.emit([ - DataFromPlugins(name='NI AI', data=data_tot, dim='Data1D', - labels=channels_name, - axes=[Axis(data=np.linspace(0, N / self.clock_settings.frequency, N), - index=0)])]) - return 0 #mandatory for the PyDAQmx callback + if not len(self.controller.task.channels.channel_names) != 1: + data_dfp = [np.array(data_from_task)] + else: + data_dfp = list(map(np.array, data_from_task)) + dte = DataToExport(name='NIDAQmx', + data=[DataFromPlugins(name='NI Analog Input', + data=data_dfp, + dim=f'Data{self.settings.child("display").value()}', + labels=channels_names + ), + ]) + self.dte_signal.emit(dte) + return 0 # mandatory for the NIDAQmx callback def counter_done(self): channels_name = [ch.name for ch in self.channels] From 697d4bed5a24c366a37738c811939ce0ca544c31 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Thu, 5 Dec 2024 17:31:47 +0100 Subject: [PATCH 19/57] PEP + syntax --- .../national_instruments/daq_NIDAQmx.py | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index 558b1a9..d5f2e80 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -307,7 +307,6 @@ def __init__(self): self.clock_settings = None self.trigger_settings = None self.live = False - self.refresh_hardware() def commit_settings(self, param: Parameter): """ @@ -490,6 +489,7 @@ def __init__(self, parent=None, params_state=None, control_type="0D"): DAQ_Viewer_base.__init__(self, parent, params_state) # defines settings attribute and various other methods DAQ_NIDAQmx_base.__init__(self) + self.Naverage = None self.live = False self.control_type = control_type # could be "0D", "1D" or "Actuator" if self.control_type == "0D": @@ -500,9 +500,10 @@ def __init__(self, parent=None, params_state=None, control_type="0D"): elif self.control_type == "Actuator": self.settings.child('NIDAQ_type').setLimits(['Analog_Output']) + self.settings.child('ao_settings').hide() self.settings.child('ao_channels').hide() - #timer used for the counter + # timer used for the counter self.timer = QtCore.QTimer() self.timer.setSingleShot(True) self.timer.timeout.connect(self.counter_done) @@ -547,7 +548,6 @@ def ini_detector(self, controller=None): -------- daq_utils.ThreadCommand """ - self.status.update(edict(initialized=False, info="", x_axis=None, y_axis=None, controller=None)) try: self.controller = self.ini_detector_init(controller, DAQmx()) self.update_task() @@ -555,16 +555,16 @@ def ini_detector(self, controller=None): # actions to perform in order to set properly the settings tree options self.commit_settings(self.settings.child('NIDAQ_type')) - self.status.info = "Plugin Initialized" - self.status.initialized = True - self.status.controller = controller - return self.status + info = "Plugin Initialized" + initialized = True + return info, initialized except Exception as e: + logger.info(traceback.format_exc()) self.emit_status(ThreadCommand('Update_Status', [str(e), 'log'])) - self.status.info = str(e) - self.status.initialized = False - return self.status + info = str(e) + initialized = False + return info, initialized def grab_data(self, Naverage=1, **kwargs): """ @@ -587,6 +587,10 @@ def grab_data(self, Naverage=1, **kwargs): if kwargs['live'] != self.live: update = True self.live = kwargs['live'] + + if Naverage != self.Naverage: + self.Naverage = Naverage + update = True if update: self.update_task() From 7a317b7eaf8184b7680af20415bf78d9d69e8e92 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Thu, 5 Dec 2024 17:35:13 +0100 Subject: [PATCH 20/57] Optimizing plugin structure by separating Move & Viewer from NIDAQmx base --- .../plugins_0D/daq_0Dviewer_NIDAQmx.py | 15 +- .../national_instruments/daq_NIDAQmx.py | 19 +- .../national_instruments/daq_NIDAQmx_Move.py | 248 ++++++++++++++++++ .../daq_NIDAQmx_Viewer.py | 179 +++++++++++++ 4 files changed, 440 insertions(+), 21 deletions(-) create mode 100644 src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Move.py create mode 100644 src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py index 3d3b1cf..5f3626c 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py @@ -1,13 +1,14 @@ -import numpy as np -from pymodaq.utils.daq_utils import ThreadCommand -from pymodaq.utils.data import DataWithAxes, DataToExport, DataSource, DataFromPlugins -from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters, main -from pymodaq.utils.parameter import Parameter -from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import * +from pymodaq.control_modules.viewer_utility_classes import main +from pymodaq.control_modules.viewer_utility_classes import comon_parameters as viewer_params +from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import AIChannel, AIThermoChannel, DAQmx, nidaqmx,\ + DAQ_termination, DAQ_thermocouples +from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_base +from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx_Viewer import DAQ_NIDAQmx_Viewer from pymodaq.utils.logger import set_logger, get_module_name logger = set_logger(get_module_name(__file__)) -class DAQ_0DViewer_NIDAQmx(DAQ_Viewer_base): + +class DAQ_0DViewer_NIDAQmx(DAQ_NIDAQmx_Viewer): """ Plugin for a 0D data visualization & acquisition with various NI modules plugged in a NI cDAQ. """ diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index d5f2e80..c08509f 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -1,20 +1,11 @@ -from qtpy import QtWidgets, QtCore -from qtpy.QtCore import Signal, QThread -from pymodaq.utils.daq_utils import ThreadCommand, getLineInfo -from pymodaq.utils.data import DataFromPlugins, Axis, DataActuator, DataToExport -import numpy as np -from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base -from pymodaq.control_modules.move_utility_classes import DAQ_Move_base -from easydict import EasyDict as edict - -from pymodaq.control_modules.viewer_utility_classes import comon_parameters as viewer_params -from pymodaq.control_modules.move_utility_classes import comon_parameters_fun as actuator_params - +from qtpy import QtWidgets +from qtpy.QtCore import Signal +from pymodaq.utils.logger import set_logger, get_module_name from pymodaq.utils.parameter import Parameter from pymodaq.utils.parameter.pymodaq_ptypes import registerParameterType, GroupParameter - -from .daqmx import DAQmx, DAQ_analog_types, DAQ_thermocouples, DAQ_termination, Edge, DAQ_NIDAQ_source, \ +from .daqmxni import DAQmx, DAQ_analog_types, DAQ_thermocouples, DAQ_termination, Edge, DAQ_NIDAQ_source, \ ClockSettings, AIChannel, Counter, AIThermoChannel, AOChannel, TriggerSettings, DOChannel, DIChannel +logger = set_logger(get_module_name(__file__)) class ScalableGroupAI(GroupParameter): diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Move.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Move.py new file mode 100644 index 0000000..35d699c --- /dev/null +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Move.py @@ -0,0 +1,248 @@ +import numpy as np +from easydict import EasyDict as edict +from qtpy import QtWidgets +from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, comon_parameters as actuator_params +from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_base +from pymodaq.utils.daq_utils import ThreadCommand, getLineInfo +from pymodaq.utils.data import DataActuator +from pymodaq.utils.logger import set_logger, get_module_name +logger = set_logger(get_module_name(__file__)) + + +class DAQ_NIDAQmx_Actuator(DAQ_Move_base, DAQ_NIDAQmx_base): + """ + Wrapper object to access the Mock fonctionnalities, similar wrapper for all controllers. + + =============== ============== + **Attributes** **Type** + *params* dictionnary + =============== ============== + """ + _controller_units = 'Volts' + is_multiaxes = False # set to True if this plugin is controlled for a multiaxis controller (with a unique communication link) + stage_names = [] # "list of strings of the multiaxes + + params = DAQ_NIDAQmx_base.params + [ + # elements to be added here as dicts in order to control your custom stage + ############ + {'title': 'MultiAxes:', 'name': 'multiaxes', 'type': 'group', 'visible': is_multiaxes, + 'children': [ + {'title': 'is Multiaxes:', 'name': 'ismultiaxes', 'type': 'bool', 'value': is_multiaxes, + 'default': False}, + {'title': 'Status:', 'name': 'multi_status', 'type': 'list', 'value': 'Master', + 'limits': ['Master', 'Slave']}, + {'title': 'Axis:', 'name': 'axis', 'type': 'list', 'limits': stage_names}, + + ]}] + actuator_params + + def __init__(self, parent=None, params_state=None, control_type="Actuator"): + DAQ_Move_base.__init__(self, parent, params_state) # defines settings attribute and various other methods + DAQ_NIDAQmx_base.__init__(self) + + self.control_type = "Actuator" # could be "0D", "1D" or "Actuator" + self.settings.child('NIDAQ_type').setLimits(['Analog_Output', 'Digital_Output']) + + self.settings.child('clock_settings', 'Nsamples').setValue(1) + + def get_actuator_value(self) -> DataActuator: + """Get the current position from the hardware with scaling conversion. + + Returns + ------- + float: The position obtained after scaling conversion. + """ + + pos = self.target_position + ## + + pos = self.get_position_with_scaling(pos) + self.emit_status(ThreadCommand('check_position', [pos])) + return pos + + def commit_settings(self, param): + """ + | Activate any parameter changes on the PI_GCS2 hardware. + | + | Called after a param_tree_changed signal from DAQ_Move_main. + + """ + + DAQ_NIDAQmx_base.commit_settings(self, param) + if param.name() == 'waveform': + if param.value() == 'DC': + self.settings.child('ao_settings', 'cont_param').setValue('offset') + self.settings.child('ao_settings', 'cont_param').show(not param.value() == 'DC') + self.settings.child('ao_settings', 'waveform_settings').show(not param.value() == 'DC') + + if param.parent() is not None: + if param.parent().name() == 'ao_channels': + device = param.opts['title'].split('/')[0] + self.settings.child('clock_settings', 'frequency').setOpts(max=self.getAOMaxRate(device)) + + ranges = self.getAOVoltageRange(device) + param.child('voltage_settings', 'volt_min').setOpts(limits=[r[0] for r in ranges]) + param.child('voltage_settings', 'volt_max').setOpts(limits=[r[1] for r in ranges]) + + def ini_stage(self, controller=None): + """Actuator communication initialization + + Parameters + ---------- + controller: (object) custom object of a PyMoDAQ plugin (Slave case). None if only one actuator by controller (Master case) + + Returns + ------- + self.status (edict): with initialization status: three fields: + * info (str) + * controller (object) initialized controller + *initialized: (bool): False if initialization failed otherwise True + """ + + try: + # initialize the stage and its controller status + # controller is an object that may be passed to other instances of DAQ_Move_Mock in case + # of one controller controlling multiactuators (or detector) + + self.status.update(edict(info="", controller=None, initialized=False)) + + # check whether this stage is controlled by a multiaxe controller (to be defined for each plugin) + # if multiaxes then init the controller here if Master state otherwise use external controller + if self.settings['multiaxes', 'ismultiaxes'] and self.settings['multiaxes', + 'multi_status'] == "Slave": + if controller is None: + raise Exception('no controller has been defined externally while this axe is a slave one') + else: + self.controller = controller + else: + self.controller = 'A Nidaqmx task' + self.update_task() + + # actions to perform in order to set properly the settings tree options + self.commit_settings(self.settings.child('NIDAQ_type')) + + self.status.info = "Plugin Initialized" + self.status.controller = self.controller + self.status.initialized = True + return self.status + + except Exception as e: + self.emit_status(ThreadCommand('Update_Status', [getLineInfo() + str(e), 'log'])) + self.status.info = getLineInfo() + str(e) + self.status.initialized = False + return self.status + + def calulate_waveform(self, value): + waveform = self.settings['ao_settings', 'waveform'] + if waveform == 'DC': + values = np.array([value]) + else: + Nsamples = self.settings['clock_settings', 'Nsamples'] + freq = self.settings['clock_settings', 'frequency'] + time = np.linspace(0, Nsamples / freq, Nsamples, endpoint=False) + + freq0 = self.settings['ao_settings', 'waveform_settings', 'frequency'] + amp = self.settings['ao_settings', 'waveform_settings', 'amplitude'] + offset = self.settings['ao_settings', 'waveform_settings', 'offset'] + if waveform == 'Sinus': + values = offset + amp * np.sin(2 * np.pi * freq0 * time) + elif waveform == 'Ramp': + values = offset + amp * np.linspace(0, 1, Nsamples) + + return values + + def move_Abs(self, position): + """ Move the actuator to the absolute target defined by position + + Parameters + ---------- + position: (flaot) value of the absolute target positioning + """ + + position = self.check_bound(position) # if user checked bounds, the defined bounds are applied here + position = self.set_position_with_scaling(position) # apply scaling if the user specified one + if self.settings['NIDAQ_type'] == 'Analog_Output': + self.settings.child('ao_settings', 'waveform_settings', + self.settings['ao_settings', 'cont_param']).setValue(position) + values = self.calulate_waveform(position) + self.target_position = position + + self.stop() + + if len(values) == 1: + self.writeAnalog(len(values), 1, values, autostart=True) + self.current_position = self.check_position() + self.move_done() + else: + if self.c_callback is None: + self.register_callback(self.move_done_callback) + self.writeAnalog(len(values), 1, values, autostart=False) + self.task.StartTask() + elif self.settings['NIDAQ_type'] == 'Digital_Output': + self.writeDigital(1, np.array([position], dtype=np.uint8), autostart=True) + + def move_done_callback(self, taskhandle, status, callbackdata): + self.current_position = self.check_position() + QtWidgets.QApplication.processEvents() + self.move_done() + self.task.StopTask() + return 0 + + def move_Rel(self, position): + """ Move the actuator to the relative target actuator value defined by position + + Parameters + ---------- + position: (flaot) value of the relative target positioning + """ + + position = self.check_bound(self.current_position + position) - self.current_position + self.target_position = position + self.current_position + if self.settings['NIDAQ_type'] == 'Analog_Output': + self.settings.child('ao_settings', 'waveform_settings', + self.settings['ao_settings', 'cont_param']).setValue(self.target_position) + + values = self.calulate_waveform(self.target_position) + + self.stop() + + if len(values) == 1: + self.writeAnalog(len(values), 1, values, autostart=True) + self.current_position = self.check_position() + self.move_done() + else: + if self.c_callback is None: + self.register_callback(self.move_done_callback) + self.writeAnalog(len(values), 1, values, autostart=False) + self.task.StartTask() + elif self.settings['NIDAQ_type'] == 'Digital_Output': + self.writeDigital(1, np.array([self.target_position], dtype=np.uint8), autostart=True) + + def move_Home(self): + """ + Send the update status thread command. + See Also + -------- + daq_utils.ThreadCommand + """ + + self.stop() + + if self.c_callback is None: + self.register_callback(self.move_done_callback) + self.writeAnalog(1, 1, np.array([0.])) + self.task.StartTask() + + def stop_motion(self): + """ + Call the specific move_done function (depending on the hardware). + + See Also + -------- + move_done + """ + + ## TODO for your custom plugin + self.stop() + self.emit_status(ThreadCommand('Update_Status', ['Some info you want to log'])) + self.move_done() # to let the interface know the actuator stopped + ############################## diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py new file mode 100644 index 0000000..afc3364 --- /dev/null +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py @@ -0,0 +1,179 @@ +import datetime +import numpy as np +import traceback +from qtpy import QtCore +from .daqmxni import DAQmx +from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_base +from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters as viewer_params +from pymodaq.utils.daq_utils import ThreadCommand +from pymodaq.utils.data import DataFromPlugins, DataToExport +from pymodaq.utils.logger import set_logger, get_module_name +logger = set_logger(get_module_name(__file__)) + + +class DAQ_NIDAQmx_Viewer(DAQ_Viewer_base, DAQ_NIDAQmx_base): + """ + ==================== ======================== + **Attributes** **Type** + *data_grabed_signal* instance of Signal + *params* dictionnary list + *task* + ==================== ======================== + + See Also + -------- + refresh_hardware + """ + + live_mode_available = True + params = viewer_params + DAQ_NIDAQmx_base.params + + def __init__(self, parent=None, params_state=None, control_type="0D"): + DAQ_Viewer_base.__init__(self, parent, params_state) # defines settings attribute and various other methods + DAQ_NIDAQmx_base.__init__(self) + + self.Naverage = None + self.live = False + self.control_type = control_type # could be "0D", "1D" or "Actuator" + if self.control_type == "0D": + self.settings.child('NIDAQ_type').setLimits( + ['Analog_Input', 'Counter', 'Digital_Input']) # analog input and counter + elif self.control_type == "1D": + self.settings.child('NIDAQ_type').setLimits(['Analog_Input']) + elif self.control_type == "Actuator": + self.settings.child('NIDAQ_type').setLimits(['Analog_Output']) + + self.settings.child('ao_settings').hide() + self.settings.child('ao_channels').hide() + + # timer used for the counter + self.timer = QtCore.QTimer() + self.timer.setSingleShot(True) + self.timer.timeout.connect(self.counter_done) + + def stop(self): + """Stop the current grab hardware wise if necessary""" + self.controller.stop() + self.live = False + try: + self.controller.task.StopTask() + logger.info("StopTask() done") + except Exception: + pass + ############################## + + self.emit_status(ThreadCommand('Update_Status', ['Acquisition stopped.'])) + return '' + + def commit_settings(self, param): + """ + Activate the parameters changes in the hardware. + + =============== ================================ =========================== + **Parameters** **Type** **Description** + *param* instance of pyqtgraph.parameter the parameter to activate + =============== ================================ =========================== + + See Also + -------- + update_NIDAQ_channels, update_task, DAQ_NIDAQ_source, refresh_hardware + """ + + if param.parent() is not None: + if param.parent().name() == 'ai_channels': + device = param.opts['title'].split('/')[0] + self.settings.child('clock_settings', 'frequency').setOpts(max=self.getAIMaxRate(device)) + + ranges = self.controller.getAIVoltageRange(device) + param.child('voltage_settings', 'volt_min').setOpts(limits=[r[0] for r in ranges]) + param.child('voltage_settings', 'volt_max').setOpts(limits=[r[1] for r in ranges]) + + DAQ_NIDAQmx_base.commit_settings(self, param) + + def ini_detector(self, controller=None): + """ + Initialisation procedure of the detector. + + See Also + -------- + daq_utils.ThreadCommand + """ + try: + self.controller = self.ini_detector_init(controller, DAQmx()) + self.update_task() + + # actions to perform in order to set properly the settings tree options + self.commit_settings(self.settings.child('NIDAQ_type')) + + info = "Plugin Initialized" + initialized = True + return info, initialized + + except Exception as e: + logger.info(traceback.format_exc()) + self.emit_status(ThreadCommand('Update_Status', [str(e), 'log'])) + info = str(e) + initialized = False + return info, initialized + + def grab_data(self, Naverage=1, **kwargs): + """ + | grab the current values with NIDAQ profile procedure. + | + | Send the data_grabed_signal once done. + + =============== ======== =============================================== + **Parameters** **Type** **Description** + *Naverage* int Number of values to average + =============== ======== =============================================== + + See Also + -------- + DAQ_NIDAQ_source + """ + update = False + + if 'live' in kwargs: + if kwargs['live'] != self.live: + update = True + self.live = kwargs['live'] + + if Naverage != self.Naverage: + self.Naverage = Naverage + update = True + if update: + self.update_task() + + if self.controller.task is None: + self.update_task() + + self.controller.register_callback(self.emit_data, "Nsamples", self.clock_settings.Nsamples) + self.controller.start() + + def emit_data(self, task_handle, every_n_samples_event_type, number_of_samples, callback_data): + channels_names = [ch.name for ch in self.channels] + # channels_ai_names = [ch.name for ch in self.channels if ch.source == 'Analog_Input'] + data_from_task = self.controller.task.read(self.settings['nsamplestoread'], timeout=20.0) + if self.control_type == "0D": + if not len(self.controller.task.channels.channel_names) != 1: + data_dfp = [np.array(data_from_task)] + else: + data_dfp = list(map(np.array, data_from_task)) + dte = DataToExport(name='NIDAQmx', + data=[DataFromPlugins(name='NI Analog Input', + data=data_dfp, + dim=f'Data{self.settings.child("display").value()}', + labels=channels_names + ), + ]) + self.dte_signal.emit(dte) + return 0 # mandatory for the NIDAQmx callback + + def counter_done(self): + channels_name = [ch.name for ch in self.channels] + data_counter = self.readCounter(len(self.channels), + self.settings['counter_settings', 'counting_time'] * 1e-3) + self.data_grabed_signal.emit([DataFromPlugins(name='NI Counter', data=[data_counter / 1e-3], dim='Data0D', + labels=channels_name, )]) + # y_axis=Axis(label='Count Number', units='1/s'))]) + self.task.StopTask() From 8288ab9abec99fb596291f2fb23d87bb6f6d5270 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Thu, 5 Dec 2024 17:36:36 +0100 Subject: [PATCH 21/57] Attributes & methods from inheritance --- .../plugins_0D/daq_0Dviewer_NIDAQmx.py | 187 ++---------------- 1 file changed, 12 insertions(+), 175 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py index 5f3626c..266bd0a 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py @@ -12,205 +12,42 @@ class DAQ_0DViewer_NIDAQmx(DAQ_NIDAQmx_Viewer): """ Plugin for a 0D data visualization & acquisition with various NI modules plugged in a NI cDAQ. """ + channels_ai: str clock_settings_ai: str dict_device_input: dict live: bool + data_tot: list + Naverage: int + ind_average: int - params = comon_parameters+[ + params = viewer_params + [ {'title': 'Display type:', 'name': 'display', 'type': 'list', 'limits': ['0D', '1D']}, {'title': 'Module ref. :', 'name': 'module', 'type': 'list', 'limits': DAQmx.get_NIDAQ_devices()[1], 'value': DAQmx.get_NIDAQ_devices()[1][0] }, - {'title': 'Analog Source :', 'name': 'ai_srce', 'type': 'list', - 'limits': DAQ_NIDAQ_source.names(), - 'value': DAQ_NIDAQ_source.names()[0] - }, - {'title': 'Analog Type :', 'name': 'ai_type', 'type': 'list', - 'limits': DAQ_analog_types.names(), - 'value': DAQ_analog_types.names()[0] - }, - {'title': 'Min. value:', 'name': 'ai_min', 'type': 'float', 'value': -80e-3, 'min': -1e4}, - {'title': 'Max. value:', 'name': 'ai_max', 'type': 'float', 'value': 80e-3, 'max': 1e4}, - {'title': 'Clock settings:', 'name': 'clock_settings', 'type': 'group', 'children': [ - {'title': 'Frequency Acq.:', 'name': 'frequency', 'type': 'int', 'value': 3, 'min': 1}, - {'title': 'Nsamples:', 'name': 'Nsamples', 'type': 'int', 'value': 10, 'default': 100, 'min': 1}, - {'title': 'Acquisition mode:', 'name': 'acqmode', 'type': 'list', - 'limits': ['Continuous', 'Finite'], 'value': 'Continous'}, - {"title": "Counting channel:", "name": "counter_channel", "type": "list", - "limits": DAQmx.get_NIDAQ_channels(source_type="Counter")}, - ]}, - ] + ] + DAQ_NIDAQmx_base.params def __init__(self, parent=None, params_state=None): super().__init__(parent, params_state) def ini_attributes(self): + super().ini_attributes() self.controller: DAQmx = None self.channels_ai = None self.clock_settings_ai = None self.dict_device_input = None self.live = False # True during a continuous grab - - def commit_settings(self, param: Parameter): - """Apply the consequences of a change of value in the detector settings - - Parameters - ---------- - param: Parameter - A given parameter (within detector_settings) whose value has been changed by the user - """ - # if param.name() == "frequency": - self.update_tasks() - - def ini_detector(self, controller=None): - """Detector communication initialization - - Parameters - ---------- - controller: (object) - custom object of a PyMoDAQ plugin (Slave case). None if only one actuator/detector by controller - (Master case) - - Returns - ------- - info: str - initialized: bool - False if initialization failed otherwise True - """ - logger.info("Detector 0D initialized") - - try: - self.controller = self.ini_detector_init(controller, DAQmx()) - self.update_tasks() - initialized = True - info = "DAQ_0D initialized" - self.controller.update_NIDAQ_devices() - self.controller.update_NIDAQ_channels() - logger.info("Detected devices: {}".format(self.controller.devices)) - self.settings.child('devices').setValue(str(self.controller.devices)) - logger.info("Detected channels: {}".format(self.controller.channels)) - self.settings.child('channels').setValue(str(self.controller.channels)) - except Exception as err: - logger.error(err) - initialized = False - info = "Error" - return info, initialized + self.data_tot = None + self.Naverage = 1 + self.ind_average = 0 def close(self): - """Terminate the communication protocol""" + self.live = False + self.controller.stop() self.controller.close() pass - def grab_data(self, Naverage=1, **kwargs): - """Start a grab from the detector - - Parameters - ---------- - Naverage: int - Number of hardware averaging not relevant here. - kwargs: dict - others optionals arguments - """ - self.update_tasks() - - data_from_task = self.controller._task.read() - logger.info("DATA = {}".format(data_from_task)) - - self.emit_data(data_from_task) - - def emit_data(self, data_measurement): - logger.info("OOOOOOOOOOOOO {}".format([chan.name for chan in self.channels_ai])) - # dte = DataToExport(name='NIDAQmx', - # data=[DataFromPlugins(name='NI Analog Input 01', - # data=[np.array([data_measurement[0]])], - # # dim=f'Data{self.settings.child("display").value()}', - # dim=f'Data0D', - # labels=[self.channels_ai[0].name], - # ), - # ]) - dte = DataToExport(name='NIDAQmx', - data=[DataFromPlugins(name='NI Analog Input 02', - data=[np.array([data_measurement[2]])], - # dim=f'Data{self.settings.child("display").value()}', - dim=f'Data0D', - labels=[self.channels_ai[2].name], - ), - ]) - - # # Dictionary linking devices to channel's physical quantities - # self.dict_device_input = {self.controller.devices[1]: [self.channels_ai[0].name, self.channels_ai[1].name], - # self.controller.devices[3]: self.channels_ai[2].name} - # # self.controller.devices #ci_meas_types - # logger.info("DICT DEVICE LABEL {}".format(self.dict_device_input)) - # logger.info(self.dict_device_input.get(self.controller.devices[1])) - # a = 0 - # b = 0 - # gen = [[ai for ai in self.dict_device_input.get(device)] - # for device in self.dict_device_input if not isinstance(self.dict_device_input.get(device), str)] + \ - # [self.dict_device_input.get(device) - # for device in self.dict_device_input if isinstance(self.dict_device_input.get(device), str)] - # - # logger.info("DATA TO EXPORT = {}".format(gen)) - # - # - # - # dte = DataToExport(name='NIDAQmx', - # data=[DataFromPlugins(name=self.dict_device_input[device], - # data=[np.array([data_measurement[i]]) - # for i in range(len(data_measurement))], - # dim='Data0D', - # labels=[ai for ai in self.dict_device_input.get(device)] - # ) for device in self.dict_device_input]) - self.dte_signal.emit(dte) - - def stop(self): - """Stop the current grab hardware wise if necessary""" - self.close() - self.emit_status(ThreadCommand('Update_Status', ['Acquisition stopped.'])) - return '' - - def update_tasks(self): - """Set up the tasks in the NI card.""" - # Create channels - ##################### - ''' - Idée: - for Device in Devices: - self.channels_from_device(Device01) = [AIchannel(),AIchannel()......] - self.channels_from_device(Device02) = [AIthermochannel(),AIthermochannel()......] - Emit by mode (as for keithley) === by device - - ''' - self.channels_ai = [AIThermoChannel(name="cDAQ1Mod1/ai0", - source='Analog_Input', - analog_type='Thermocouple', - value_min=-20., - value_max=1300., - thermo_type=DAQ_thermocouples.K, - ), - AIThermoChannel(name="cDAQ1Mod1/ai1", - source='Analog_Input', - analog_type='Thermocouple', - value_min=-20., - value_max=1300., - thermo_type=DAQ_thermocouples.K, - ), - AIChannel(name="cDAQ1Mod3/ai0", - source='Analog_Input', - analog_type='Voltage', - value_min=-10., - value_max=10., - termination=DAQ_termination.Diff, - ), - ] - self.clock_settings_ai = ClockSettings(frequency=self.settings.child('clock_settings', 'frequency').value(), - Nsamples=self.settings.child('clock_settings', 'Nsamples').value(), - edge=Edge.Rising, - repetition=self.live) - - self.controller.update_task(self.channels_ai, self.clock_settings_ai) - if __name__ == '__main__': """Main section used during development tests""" From e0559a36b98d691f0a657cf364e1e5d52d594714 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Thu, 5 Dec 2024 17:39:21 +0100 Subject: [PATCH 22/57] Main file reworked for callback tests --- .../plugins_0D/daq_0Dviewer_NIDAQmx.py | 153 +++++++++++------- 1 file changed, 93 insertions(+), 60 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py index 266bd0a..6abe3e5 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py @@ -51,74 +51,107 @@ def close(self): if __name__ == '__main__': """Main section used during development tests""" - main_file = True + main_file = False if main_file: main(__file__) else: try: print("In main") - import nidaqmx - - logger.info("DAQ Sources names{}".format(DAQ_NIDAQ_source.names())) - logger.info("DAQ Sources members{}".format(DAQ_NIDAQ_source.members())) - logger.info("DAQ Sources names0{}".format(DAQ_NIDAQ_source.names()[0])) - logger.info("DAQ Sources members0{}".format(DAQ_NIDAQ_source.members()[0])) - channels = [AIThermoChannel(name="cDAQ1Mod1/ai0", - source='Analog_Input', - analog_type='Thermocouple', - value_min=-80.0e-3, - value_max=80.0e-3, - termination='Diff', - thermo_type=DAQ_thermocouples.K), - ] - # Create Task - print("DAQ_thermocouples.names ", DAQ_thermocouples.names()) - print("DAQ_thermocouples.K ", nidaqmx.constants.ThermocoupleType.K) - print("DAQ_thermocouples.K type", type(nidaqmx.constants.ThermocoupleType.K)) - print("DAQ_thermocouples.K ", DAQ_thermocouples.K) - print("DAQ_thermocouples.K type ", type(DAQ_thermocouples.K)) - print("channel.thermo_type ", channels[0].thermo_type) - task = nidaqmx.Task() - for channel in channels: - task.ai_channels.add_ai_thrmcpl_chan(physical_channel=channel.name, - # name_to_assign_to_channel="Channel 01", - min_val=channel.value_min, - max_val=channel.value_max, - units=TemperatureUnits.DEG_C, - thermocouple_type=channel.thermo_type, - # cjc_source=CJCSource.BUILT_IN, - # cjc_val="", - # cjc_channel="", - ) - NIDAQ_Devices = nidaqmx.system.System.local().devices - - print("NIDAQ devices ", NIDAQ_Devices) - print("NIDAQ devices names ", NIDAQ_Devices.device_names) - - Chassis = NIDAQ_Devices[0] - Module01 = NIDAQ_Devices[1] - Module02 = NIDAQ_Devices[2] - print("Chassis={}, Module01={}, Module02={}" .format(Chassis, Module01, Module02)) - - # Test resources + import nidaqmx as ni + from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import CurrentUnits, TemperatureUnits,\ + VoltageUnits, CJCSource + + # EXPLORE DEVICES + devices = ni.system.System.local().devices + print("devices {}".format(devices)) + print("devices names {}".format(devices.device_names)) + print("devices types {}".format([dev.product_type for dev in devices])) + cdaq = devices[0] + mod1 = cdaq.chassis_module_devices[0] # Equivalent devices[1] + mod2 = devices[2] + mod3 = devices[3] try: - Chassis.self_test_device() - Module01.self_test_device() - Module02.self_test_device() + usb1 = devices[4] except Exception as e: - print("Resources test failed: {}" .format(e)) - - print("Chassis: name={}, Num={}".format(Chassis.name, Chassis.product_type)) - print("Module01: name={}, Num={}".format(Module01.name, Module01.product_type)) - print("Module02: name={}, Num={}".format(Module02.name, Module02.product_type)) + pass + print("cDAQ modules: {}".format(mod.compact_daq_chassis_device.product_type for mod in [mod1, mod2, mod3])) - print("channel 01 name : ", channels[0].name) - data = task.read() - print("data = ", data) - print("type(data) = ", type(data)) - print("type(data[0]) = ", type(data[0])) + # TEST RESOURCES + try: + for device in devices: + device.self_test_device() + except Exception as e: + print("Resources test failed: {}" .format(e)) - task.close() + # CREATE CHANNELS + channels_th = [AIThermoChannel(name="cDAQ1Mod1/ai0", + source='Analog_Input', + analog_type='Thermocouple', + value_min=-100, + value_max=1000, + thermo_type=DAQ_thermocouples.K), + ] + channels_voltage = [AIChannel(name="cDAQ1Mod3/ai0", + source='Analog_Input', + analog_type='voltage', + value_min=-80.0e-3, + value_max=80.0e-3, + termination=DAQ_termination.Auto, + ), + AIChannel(name="cDAQ1Mod3/ai1", + source='Analog_Input', + analog_type='voltage', + value_min=-80.0e-3, + value_max=80.0e-3, + termination=DAQ_termination.Auto, + ), + ] + # CREATE TASK + task_9211 = nidaqmx.Task() + task_9205 = nidaqmx.Task() + + def callback_9211(task_handle, every_n_samples_event_type, number_of_samples, callback_data): + data9211 = task_9211.read(5) + print(data9211) + + def callback_9205(task_handle, every_n_samples_event_type, number_of_samples, callback_data): + data9205 = task_9205.read(5) + print(data9205) + + for channel in channels_th: + task_9211.ai_channels.add_ai_thrmcpl_chan(channel.name, + "", + channel.value_min, + channel.value_max, + TemperatureUnits.DEG_C, + channel.thermo_type, + CJCSource.BUILT_IN, + 0., + "") + for channel in channels_voltage: + task_9205.ai_channels.add_ai_voltage_chan(channel.name, + "", + channel.termination, + channel.value_min, + channel.value_max, + VoltageUnits.VOLTS, + "") + task_9211.timing.cfg_samp_clk_timing(5.0, None, nidaqmx.constants.Edge.RISING, + nidaqmx.constants.AcquisitionType.CONTINUOUS, 5) + task_9211.register_every_n_samples_acquired_into_buffer_event(10, callback_9211) + + task_9205.timing.cfg_samp_clk_timing(10, None, nidaqmx.constants.Edge.RISING, + nidaqmx.constants.AcquisitionType.CONTINUOUS, 10) + task_9205.register_every_n_samples_acquired_into_buffer_event(2, callback_9205) + + task_9211.start() + task_9205.start() + + print("Acquisition in progress... Press enter to stop") + input() + + task_9211.close() + task_9205.close() except Exception as e: - print("Exception ({}): {}".format(type(e), str(e))) \ No newline at end of file + print("Exception ({}): {}".format(type(e), str(e))) From f0e25f3e855e030b2b5d081ead5d3aa5bcbf6035 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Fri, 6 Dec 2024 10:10:09 +0100 Subject: [PATCH 23/57] add config files .toml --- .../resources/config_template.toml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/pymodaq_plugins_daqmx/resources/config_template.toml b/src/pymodaq_plugins_daqmx/resources/config_template.toml index cf493ee..4a84012 100644 --- a/src/pymodaq_plugins_daqmx/resources/config_template.toml +++ b/src/pymodaq_plugins_daqmx/resources/config_template.toml @@ -1,2 +1,28 @@ title = 'Configuration file of the DAQmx plugin' +[NIDAQ_Devices] + +[NIDAQ_Devices.DEVICE01] +title = "Configuration entry for a NIDAQmx device" +name = "" +product = "" + +[NIDAQ_Devices.DEVICE01.MODULE01] +title = "A module plugged in the NIDAQmx device" +name = "" +product = "" + +[NIDAQ_Devices.DEVICE01.MODULE02] +title = "A module plugged in a NIDAQmx device" +name = "" +product = "" + +[NIDAQ_Devices.DEVICE01.MODULE03] +title = "A module plugged in a NIDAQmx device" +name = "" +product = "" + +[NIDAQ_Devices.DEVICE01.MODULE01.ai.ai0] +current_fields_example = "source, analog_type, value_min, value_max, termination" +voltage_fields_example = "source, analog_type, value_min, value_max, termination" +thrmcpl_fields_example = "source, analog_type, value_min, value_max, thermo_type" \ No newline at end of file From 73717745b1dc756ac8b55fed8e9347c7bbcc97cc Mon Sep 17 00:00:00 2001 From: Sebastien Date: Fri, 6 Dec 2024 11:45:46 +0100 Subject: [PATCH 24/57] Fixed imports --- .../daq_move_plugins/daq_move_DAQmx.py | 2 +- .../daq_move_DAQmx_MultipleScannerControl.py | 1 + .../daq_move_DAQmx_ScannerControl.py | 1 + .../plugins_0D/daq_0Dviewer_DAQmx.py | 2 +- .../plugins_0D/daq_0Dviewer_DAQmx_counter.py | 2 + .../plugins_1D/daq_1Dviewer_DAQmx.py | 2 +- .../national_instruments/daq_NIDAQmx.py | 397 ------------------ 7 files changed, 7 insertions(+), 400 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_DAQmx.py b/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_DAQmx.py index 0fb267d..f5e26db 100644 --- a/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_DAQmx.py +++ b/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_DAQmx.py @@ -1,4 +1,4 @@ -from ..hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_Actuator +from ..hardware.national_instruments.daq_NIDAQmx_Move import DAQ_NIDAQmx_Actuator class DAQ_Move_DAQmx(DAQ_NIDAQmx_Actuator): diff --git a/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_DAQmx_MultipleScannerControl.py b/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_DAQmx_MultipleScannerControl.py index 3e8bc0c..e95e90e 100644 --- a/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_DAQmx_MultipleScannerControl.py +++ b/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_DAQmx_MultipleScannerControl.py @@ -11,6 +11,7 @@ import PyDAQmx + class DAQ_Move_DAQmx_MultipleScannerControl(DAQ_Move_base): """Plugin to control a piezo scanners with a NI card. This modules requires a clock channel to handle the timing of the movement and display the position, and this clock channel is shared between the master and diff --git a/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_DAQmx_ScannerControl.py b/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_DAQmx_ScannerControl.py index 24ca8cf..1a576e6 100644 --- a/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_DAQmx_ScannerControl.py +++ b/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_DAQmx_ScannerControl.py @@ -11,6 +11,7 @@ import PyDAQmx + class DAQ_Move_DAQmx_ScannerControl(DAQ_Move_base): """Plugin to control a piezo scanner with a NI card. This modules requires a clock channel to handle the timing of the movement and display the position. Avoid using several scanners (ie several analog outputs) diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx.py index 2c8d623..87be685 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx.py @@ -1,4 +1,4 @@ -from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_Viewer +from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx_Viewer import DAQ_NIDAQmx_Viewer from pymodaq.control_modules.viewer_utility_classes import main diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py index b66aa6a..7add9e6 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py @@ -6,6 +6,8 @@ import nidaqmx from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import DAQmx, Edge, Counter + + class DAQ_0DViewer_DAQmx_counter(DAQ_Viewer_base): """ Plugin for a 0D PL counter, based on a NI card. diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_1D/daq_1Dviewer_DAQmx.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_1D/daq_1Dviewer_DAQmx.py index fbf9020..6da3bf5 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_1D/daq_1Dviewer_DAQmx.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_1D/daq_1Dviewer_DAQmx.py @@ -1,4 +1,4 @@ -from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_Viewer +from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx_Viewer import DAQ_NIDAQmx_Viewer from pymodaq.control_modules.viewer_utility_classes import main diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index c08509f..4a32af0 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -458,400 +458,3 @@ def stop(self): self.timer.stop() QtWidgets.QApplication.processEvents() self.controller.stop() - -class DAQ_NIDAQmx_Viewer(DAQ_Viewer_base, DAQ_NIDAQmx_base): - """ - ==================== ======================== - **Attributes** **Type** - *data_grabed_signal* instance of Signal - *params* dictionnary list - *task* - ==================== ======================== - - See Also - -------- - refresh_hardware - """ - - live_mode_available = True - params = viewer_params + DAQ_NIDAQmx_base.params - - def __init__(self, parent=None, params_state=None, control_type="0D"): - DAQ_Viewer_base.__init__(self, parent, params_state) # defines settings attribute and various other methods - DAQ_NIDAQmx_base.__init__(self) - - self.Naverage = None - self.live = False - self.control_type = control_type # could be "0D", "1D" or "Actuator" - if self.control_type == "0D": - self.settings.child('NIDAQ_type').setLimits( - ['Analog_Input', 'Counter', 'Digital_Input']) # analog input and counter - elif self.control_type == "1D": - self.settings.child('NIDAQ_type').setLimits(['Analog_Input']) - elif self.control_type == "Actuator": - self.settings.child('NIDAQ_type').setLimits(['Analog_Output']) - - self.settings.child('ao_settings').hide() - self.settings.child('ao_channels').hide() - - # timer used for the counter - self.timer = QtCore.QTimer() - self.timer.setSingleShot(True) - self.timer.timeout.connect(self.counter_done) - - def stop(self): - """Stop the current grab hardware wise if necessary""" - self.controller.stop() - self.live = False - self.emit_status(ThreadCommand('Update_Status', ['Acquisition stopped.'])) - return '' - - def commit_settings(self, param): - """ - Activate the parameters changes in the hardware. - - =============== ================================ =========================== - **Parameters** **Type** **Description** - *param* instance of pyqtgraph.parameter the parameter to activate - =============== ================================ =========================== - - See Also - -------- - update_NIDAQ_channels, update_task, DAQ_NIDAQ_source, refresh_hardware - """ - - if param.parent() is not None: - if param.parent().name() == 'ai_channels': - device = param.opts['title'].split('/')[0] - self.settings.child('clock_settings', 'frequency').setOpts(max=self.getAIMaxRate(device)) - - ranges = self.controller.getAIVoltageRange(device) - param.child('voltage_settings', 'volt_min').setOpts(limits=[r[0] for r in ranges]) - param.child('voltage_settings', 'volt_max').setOpts(limits=[r[1] for r in ranges]) - - DAQ_NIDAQmx_base.commit_settings(self, param) - - def ini_detector(self, controller=None): - """ - Initialisation procedure of the detector. - - See Also - -------- - daq_utils.ThreadCommand - """ - try: - self.controller = self.ini_detector_init(controller, DAQmx()) - self.update_task() - - # actions to perform in order to set properly the settings tree options - self.commit_settings(self.settings.child('NIDAQ_type')) - - info = "Plugin Initialized" - initialized = True - return info, initialized - - except Exception as e: - logger.info(traceback.format_exc()) - self.emit_status(ThreadCommand('Update_Status', [str(e), 'log'])) - info = str(e) - initialized = False - return info, initialized - - def grab_data(self, Naverage=1, **kwargs): - """ - | grab the current values with NIDAQ profile procedure. - | - | Send the data_grabed_signal once done. - - =============== ======== =============================================== - **Parameters** **Type** **Description** - *Naverage* int Number of values to average - =============== ======== =============================================== - - See Also - -------- - DAQ_NIDAQ_source - """ - update = False - - if 'live' in kwargs: - if kwargs['live'] != self.live: - update = True - self.live = kwargs['live'] - - if Naverage != self.Naverage: - self.Naverage = Naverage - update = True - if update: - self.update_task() - - if self.controller.task is None: - self.update_task() - - self.controller.register_callback(self.emit_data, "Nsamples", self.clock_settings.Nsamples) - self.controller.start() - - def emit_data(self, task_handle, every_n_samples_event_type, number_of_samples, callback_data): - channels_names = [ch.name for ch in self.channels] - # channels_ai_names = [ch.name for ch in self.channels if ch.source == 'Analog_Input'] - data_from_task = self.controller.task.read(self.settings['nsamplestoread'], timeout=20.0) - if self.control_type == "0D": - if not len(self.controller.task.channels.channel_names) != 1: - data_dfp = [np.array(data_from_task)] - else: - data_dfp = list(map(np.array, data_from_task)) - dte = DataToExport(name='NIDAQmx', - data=[DataFromPlugins(name='NI Analog Input', - data=data_dfp, - dim=f'Data{self.settings.child("display").value()}', - labels=channels_names - ), - ]) - self.dte_signal.emit(dte) - return 0 # mandatory for the NIDAQmx callback - - def counter_done(self): - channels_name = [ch.name for ch in self.channels] - data_counter = self.readCounter(len(self.channels), - self.settings['counter_settings', 'counting_time'] * 1e-3) - self.data_grabed_signal.emit([DataFromPlugins(name='NI Counter', data=[data_counter / 1e-3], dim='Data0D', - labels=channels_name, )]) - # y_axis=Axis(label='Count Number', units='1/s'))]) - self.task.StopTask() - -class DAQ_NIDAQmx_Actuator(DAQ_Move_base, DAQ_NIDAQmx_base): - """ - Wrapper object to access the Mock fonctionnalities, similar wrapper for all controllers. - - =============== ============== - **Attributes** **Type** - *params* dictionnary - =============== ============== - """ - _controller_units = 'Volts' - is_multiaxes = False # set to True if this plugin is controlled for a multiaxis controller (with a unique communication link) - stage_names = [] # "list of strings of the multiaxes - - params = DAQ_NIDAQmx_base.params + [ - # elements to be added here as dicts in order to control your custom stage - ############ - {'title': 'MultiAxes:', 'name': 'multiaxes', 'type': 'group', 'visible': is_multiaxes, - 'children': [ - {'title': 'is Multiaxes:', 'name': 'ismultiaxes', 'type': 'bool', 'value': is_multiaxes, - 'default': False}, - {'title': 'Status:', 'name': 'multi_status', 'type': 'list', 'value': 'Master', - 'limits': ['Master', 'Slave']}, - {'title': 'Axis:', 'name': 'axis', 'type': 'list', 'limits': stage_names}, - ]}] + actuator_params - - def __init__(self, parent=None, params_state=None, control_type="Actuator"): - DAQ_Move_base.__init__(self, parent, params_state) # defines settings attribute and various other methods - DAQ_NIDAQmx_base.__init__(self) - - self.control_type = "Actuator" # could be "0D", "1D" or "Actuator" - self.settings.child('NIDAQ_type').setLimits(['Analog_Output', 'Digital_Output']) - - self.settings.child('clock_settings', 'Nsamples').setValue(1) - - def get_actuator_value(self) -> DataActuator: - """Get the current position from the hardware with scaling conversion. - - Returns - ------- - float: The position obtained after scaling conversion. - """ - - pos = self.target_position - ## - - pos = self.get_position_with_scaling(pos) - self.emit_status(ThreadCommand('check_position', [pos])) - return pos - - def commit_settings(self, param): - """ - | Activate any parameter changes on the PI_GCS2 hardware. - | - | Called after a param_tree_changed signal from DAQ_Move_main. - - """ - - DAQ_NIDAQmx_base.commit_settings(self, param) - if param.name() == 'waveform': - if param.value() == 'DC': - self.settings.child('ao_settings', 'cont_param').setValue('offset') - self.settings.child('ao_settings', 'cont_param').show(not param.value() == 'DC') - self.settings.child('ao_settings', 'waveform_settings').show(not param.value() == 'DC') - - if param.parent() is not None: - if param.parent().name() == 'ao_channels': - device = param.opts['title'].split('/')[0] - self.settings.child('clock_settings', 'frequency').setOpts(max=self.getAOMaxRate(device)) - - ranges = self.getAOVoltageRange(device) - param.child('voltage_settings', 'volt_min').setOpts(limits=[r[0] for r in ranges]) - param.child('voltage_settings', 'volt_max').setOpts(limits=[r[1] for r in ranges]) - - def ini_stage(self, controller=None): - """Actuator communication initialization - - Parameters - ---------- - controller: (object) custom object of a PyMoDAQ plugin (Slave case). None if only one actuator by controller (Master case) - - Returns - ------- - self.status (edict): with initialization status: three fields: - * info (str) - * controller (object) initialized controller - *initialized: (bool): False if initialization failed otherwise True - """ - - try: - # initialize the stage and its controller status - # controller is an object that may be passed to other instances of DAQ_Move_Mock in case - # of one controller controlling multiactuators (or detector) - - self.status.update(edict(info="", controller=None, initialized=False)) - - # check whether this stage is controlled by a multiaxe controller (to be defined for each plugin) - # if multiaxes then init the controller here if Master state otherwise use external controller - if self.settings['multiaxes', 'ismultiaxes'] and self.settings['multiaxes', - 'multi_status'] == "Slave": - if controller is None: - raise Exception('no controller has been defined externally while this axe is a slave one') - else: - self.controller = controller - else: - self.controller = 'A Nidaqmx task' - self.update_task() - - # actions to perform in order to set properly the settings tree options - self.commit_settings(self.settings.child('NIDAQ_type')) - - self.status.info = "Plugin Initialized" - self.status.controller = self.controller - self.status.initialized = True - return self.status - - except Exception as e: - self.emit_status(ThreadCommand('Update_Status', [getLineInfo() + str(e), 'log'])) - self.status.info = getLineInfo() + str(e) - self.status.initialized = False - return self.status - - def calulate_waveform(self, value): - waveform = self.settings['ao_settings', 'waveform'] - if waveform == 'DC': - values = np.array([value]) - else: - Nsamples = self.settings['clock_settings', 'Nsamples'] - freq = self.settings['clock_settings', 'frequency'] - time = np.linspace(0, Nsamples / freq, Nsamples, endpoint=False) - - freq0 = self.settings['ao_settings', 'waveform_settings', 'frequency'] - amp = self.settings['ao_settings', 'waveform_settings', 'amplitude'] - offset = self.settings['ao_settings', 'waveform_settings', 'offset'] - if waveform == 'Sinus': - values = offset + amp * np.sin(2 * np.pi * freq0 * time) - elif waveform == 'Ramp': - values = offset + amp * np.linspace(0, 1, Nsamples) - - return values - - def move_Abs(self, position): - """ Move the actuator to the absolute target defined by position - - Parameters - ---------- - position: (flaot) value of the absolute target positioning - """ - - position = self.check_bound(position) # if user checked bounds, the defined bounds are applied here - position = self.set_position_with_scaling(position) # apply scaling if the user specified one - if self.settings['NIDAQ_type'] == 'Analog_Output': - self.settings.child('ao_settings', 'waveform_settings', - self.settings['ao_settings', 'cont_param']).setValue(position) - values = self.calulate_waveform(position) - self.target_position = position - - self.stop() - - if len(values) == 1: - self.writeAnalog(len(values), 1, values, autostart=True) - self.current_position = self.check_position() - self.move_done() - else: - if self.c_callback is None: - self.register_callback(self.move_done_callback) - self.writeAnalog(len(values), 1, values, autostart=False) - self.task.StartTask() - elif self.settings['NIDAQ_type'] == 'Digital_Output': - self.writeDigital(1, np.array([position], dtype=np.uint8), autostart=True) - - def move_done_callback(self, taskhandle, status, callbackdata): - self.current_position = self.check_position() - QtWidgets.QApplication.processEvents() - self.move_done() - self.task.StopTask() - return 0 - - def move_Rel(self, position): - """ Move the actuator to the relative target actuator value defined by position - - Parameters - ---------- - position: (flaot) value of the relative target positioning - """ - - position = self.check_bound(self.current_position + position) - self.current_position - self.target_position = position + self.current_position - if self.settings['NIDAQ_type'] == 'Analog_Output': - self.settings.child('ao_settings', 'waveform_settings', - self.settings['ao_settings', 'cont_param']).setValue(self.target_position) - - values = self.calulate_waveform(self.target_position) - - self.stop() - - if len(values) == 1: - self.writeAnalog(len(values), 1, values, autostart=True) - self.current_position = self.check_position() - self.move_done() - else: - if self.c_callback is None: - self.register_callback(self.move_done_callback) - self.writeAnalog(len(values), 1, values, autostart=False) - self.task.StartTask() - elif self.settings['NIDAQ_type'] == 'Digital_Output': - self.writeDigital(1, np.array([self.target_position], dtype=np.uint8), autostart=True) - - def move_Home(self): - """ - Send the update status thread command. - See Also - -------- - daq_utils.ThreadCommand - """ - - self.stop() - - if self.c_callback is None: - self.register_callback(self.move_done_callback) - self.writeAnalog(1, 1, np.array([0.])) - self.task.StartTask() - - def stop_motion(self): - """ - Call the specific move_done function (depending on the hardware). - - See Also - -------- - move_done - """ - - ## TODO for your custom plugin - self.stop() - self.emit_status(ThreadCommand('Update_Status', ['Some info you want to log'])) - self.move_done() # to let the interface know the actuator stopped - ############################## From 7ba5d556e7dac0aa04877b135721f6f28ce354a9 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Fri, 6 Dec 2024 14:19:08 +0100 Subject: [PATCH 25/57] Import config from .toml files --- src/pymodaq_plugins_daqmx/__init__.py | 3 +++ .../hardware/national_instruments/daqmxni.py | 1 + src/pymodaq_plugins_daqmx/utils.py | 9 +++++++++ 3 files changed, 13 insertions(+) create mode 100644 src/pymodaq_plugins_daqmx/utils.py diff --git a/src/pymodaq_plugins_daqmx/__init__.py b/src/pymodaq_plugins_daqmx/__init__.py index 8a33bda..097668d 100644 --- a/src/pymodaq_plugins_daqmx/__init__.py +++ b/src/pymodaq_plugins_daqmx/__init__.py @@ -1,5 +1,8 @@ from pathlib import Path from pymodaq.utils.logger import set_logger # to be imported by other modules. +from .utils import Config +config = Config() + with open(str(Path(__file__).parent.joinpath('resources/VERSION')), 'r') as fvers: __version__ = fvers.read().strip() \ No newline at end of file diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index 806c84a..cc0dd18 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -8,6 +8,7 @@ from nidaqmx.constants import * from nidaqmx.system.device import Device from nidaqmx.errors import DaqError, DAQmxErrors +from pymodaq_plugins_daqmx import config logger = set_logger(get_module_name(__file__)) diff --git a/src/pymodaq_plugins_daqmx/utils.py b/src/pymodaq_plugins_daqmx/utils.py new file mode 100644 index 0000000..fb11e4d --- /dev/null +++ b/src/pymodaq_plugins_daqmx/utils.py @@ -0,0 +1,9 @@ +from pathlib import Path + +from pymodaq.utils.config import BaseConfig, USER + + +class Config(BaseConfig): + """Main class to deal with configuration values for this plugin""" + config_template_path = Path(__file__).parent.joinpath('resources/config_template.toml') + config_name = f"config_{__package__.split('pymodaq_plugins_')[1]}" \ No newline at end of file From 91dbcfe55ccac05651bed046c39cac21e24b053d Mon Sep 17 00:00:00 2001 From: Sebastien Date: Mon, 9 Dec 2024 22:15:29 +0100 Subject: [PATCH 26/57] Add use of toml files: config sequence + generates scalablegroup from config during init --- .../plugins_0D/daq_0Dviewer_NIDAQmx.py | 23 +++-- .../daq_NIDAQmx_Viewer.py | 44 +++++++++- .../hardware/national_instruments/daqmxni.py | 85 +++++++++++++++++++ 3 files changed, 140 insertions(+), 12 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py index 6abe3e5..fd90a15 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py @@ -1,5 +1,6 @@ from pymodaq.control_modules.viewer_utility_classes import main from pymodaq.control_modules.viewer_utility_classes import comon_parameters as viewer_params +from pymodaq_plugins_daqmx import config from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import AIChannel, AIThermoChannel, DAQmx, nidaqmx,\ DAQ_termination, DAQ_thermocouples from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_base @@ -13,9 +14,13 @@ class DAQ_0DViewer_NIDAQmx(DAQ_NIDAQmx_Viewer): Plugin for a 0D data visualization & acquisition with various NI modules plugged in a NI cDAQ. """ - channels_ai: str - clock_settings_ai: str - dict_device_input: dict + config_channels: list + channels_ai: list + config: config + controller: DAQmx + config_devices: list + config_modules: list + current_device: nidaqmx.system.Device live: bool data_tot: list Naverage: int @@ -33,12 +38,12 @@ def __init__(self, parent=None, params_state=None): def ini_attributes(self): super().ini_attributes() - self.controller: DAQmx = None - self.channels_ai = None - self.clock_settings_ai = None - self.dict_device_input = None - self.live = False # True during a continuous grab - self.data_tot = None + self.channels_ai = [] + self.config = config + self.config_channels = [] + self.config_devices = [] + self.config_modules = [] + self.live = False self.Naverage = 1 self.ind_average = 0 diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py index afc3364..0deb027 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py @@ -3,7 +3,7 @@ import traceback from qtpy import QtCore from .daqmxni import DAQmx -from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_base +from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_base, DAQ_termination from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters as viewer_params from pymodaq.utils.daq_utils import ThreadCommand from pymodaq.utils.data import DataFromPlugins, DataToExport @@ -32,6 +32,7 @@ def __init__(self, parent=None, params_state=None, control_type="0D"): DAQ_Viewer_base.__init__(self, parent, params_state) # defines settings attribute and various other methods DAQ_NIDAQmx_base.__init__(self) + self.current_device = None self.Naverage = None self.live = False self.control_type = control_type # could be "0D", "1D" or "Actuator" @@ -99,12 +100,49 @@ def ini_detector(self, controller=None): daq_utils.ThreadCommand """ try: + self.current_device = nidaqmx.system.Device(self.settings["devices"]) self.controller = self.ini_detector_init(controller, DAQmx()) - self.update_task() + self.controller.configuration_sequence(self, self.current_device) # actions to perform in order to set properly the settings tree options self.commit_settings(self.settings.child('NIDAQ_type')) - + for ch in self.config_channels: + if self.settings.child("dev_to_use").value() in ch.name: + self.settings.child('ai_channels').addNew(ch.name) + param = [a for a in self.settings.child('ai_channels').childs if a.opts['title'] == ch.name][0] + self.settings.child("ai_channels", param.opts['name'], "ai_type").setValue(ch.analog_type) + param.child("voltage_settings").show(ch.analog_type == "Voltage") + param.child("current_settings").show(ch.analog_type == "Current") + param.child("thermoc_settings").show(ch.analog_type == "Thermocouple") + if ch.analog_type == "Voltage": + self.settings.child("ai_channels", param.opts['name'], "voltage_settings", "volt_min").setValue( + ch.value_min) + self.settings.child("ai_channels", param.opts['name'], "voltage_settings", "volt_max").setValue( + ch.value_max) + self.settings.child("ai_channels", param.opts['name'], "termination").setValue( + ch.termination.name) + elif ch.analog_type == "Current": + self.settings.child("ai_channels", param.opts['name'], "current_settings", "curr_min").setValue( + ch.value_min) + self.settings.child("ai_channels", param.opts['name'], "current_settings", "curr_max").setValue( + ch.value_max) + self.settings.child("ai_channels", param.opts['name'], "termination").setValue( + ch.termination.name) + elif ch.analog_type == "Thermocouple": + self.settings.child("ai_channels", + param.opts['name'], + "thermoc_settings", + "thermoc_type").setValue(ch.thermo_type.name) + self.settings.child("ai_channels", + param.opts['name'], + "thermoc_settings", + "T_min").setValue(ch.value_min) + self.settings.child("ai_channels", + param.opts['name'], + "thermoc_settings", + "T_max").setValue(ch.value_max) + self.settings.child("ai_channels", param.opts['name'], "termination").setValue( + DAQ_termination.Auto) info = "Plugin Initialized" initialized = True return info, initialized diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index cc0dd18..0093e0a 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -351,6 +351,91 @@ def get_NIDAQ_channels(cls, devices=None, source_type=None): return channels_tot + def configuration_sequence(self, viewer, current_device): + """Configure each / modules / channels as giver by the user in the configuration file + + Read the .toml file to get the desired hardware configuration, + and send the nidaqmx a sequence which set up each channel. + """ + logger.info("********** CONFIGURATION SEQUENCE INITIALIZED **********") + devices_info = [dev.name + ': ' + dev.product_type for dev in self.devices] + logger.info("Detected devices: {}".format(devices_info)) + try: + viewer.config_devices = [config["NIDAQ_Devices", dev].get('name') for dev in viewer.config["NIDAQ_Devices"] + if "Mod" not in config["NIDAQ_Devices", dev].get('name')] + logger.info(viewer.config_devices) + for dev in config["NIDAQ_Devices"]: + if not isinstance(config["NIDAQ_Devices", dev], dict): + continue + try: + device_name = config["NIDAQ_Devices", dev].get('name') + if not device_name == current_device.name: + continue + device_product = config["NIDAQ_Devices", dev].get('product') + device = nidaqmx.system.device.Device(device_name) + assert device in self.devices and device.product_type == device_product, device.name + except AssertionError as err: + logger.error("Device {} not detected: {}".format(device_name, err)) + continue + for mod in config["NIDAQ_Devices", dev]: + if not isinstance(config["NIDAQ_Devices", dev, mod], dict): + continue + try: + module_name = config["NIDAQ_Devices", dev, mod].get('name') + module_product = config["NIDAQ_Devices", dev, mod].get('product') + module = nidaqmx.system.device.Device(module_name) + assert module in self.devices and module.product_type == module_product, module.name + viewer.config_modules.append(config["NIDAQ_Devices", dev, mod].get('name')) + except AssertionError as err: + logger.error("Module {} not detected: {}".format(module_name, err)) + continue + for source in config["NIDAQ_Devices", dev, mod]: + if not isinstance(config["NIDAQ_Devices", dev, mod, source], dict): + continue + if source == "ai": + ai = config["NIDAQ_Devices", dev, mod, source] + for ch in ai.keys(): + name = module_name + "/" + str(ch) + term = ai[ch].get("termination") + if ai[ch].get("analog_type") == "Voltage": + viewer.config_channels.append(AIChannel + (name=name, + source=ai[ch].get("source"), + analog_type=ai[ch].get("analog_type"), + value_min=float(ai[ch].get("value_min")), + value_max=float(ai[ch].get("value_max")), + termination=DAQ_termination.__getitem__(term), + )) + elif ai[ch].get("analog_type") == "Current": + viewer.config_channels.append(AIChannel + (name=name, + source=ai[ch].get("source"), + analog_type=ai[ch].get("analog_type"), + value_min=float(ai[ch].get("value_min")), + value_max=float(ai[ch].get("value_max")), + termination=DAQ_termination.__getitem__(term), + )) + elif ai[ch].get("analog_type") == "Thermocouple": + th = ai[ch].get("thermo_type") + viewer.config_channels.append(AIThermoChannel + (name=name, + source=ai[ch].get("source"), + analog_type=ai[ch].get("analog_type"), + value_min=float(ai[ch].get("value_min")), + value_max=float(ai[ch].get("value_max")), + thermo_type=DAQ_thermocouples.__getitem__(th), + )) + logger.info("Devices from config: {}".format(viewer.config_devices)) + logger.info("Current device: {}".format(current_device)) + logger.info("Current device modules from config: {}".format(viewer.config_modules)) + logger.info("Current device channels from config: {}".format([ch.name for ch in viewer.config_channels])) + except AssertionError as err: + logger.error("Configuration entries <{}> does not match the hardware ".format(err)) + except Exception as err: + logger.info("Configuration sequence error, verify if your config matches the hardware: {}".format(err)) + pass + logger.info(" ********** CONFIGURATION SEQUENCE SUCCESSFULLY ENDED **********") + @classmethod def getAOMaxRate(cls, device): return Device(device).ao_max_rate From 9d0b6733c8c871fb1601d6edf6bac66cd5c9cffd Mon Sep 17 00:00:00 2001 From: Sebastien Date: Mon, 9 Dec 2024 22:19:14 +0100 Subject: [PATCH 27/57] Minor modifs (PEP, unused imports, info logged) --- .../hardware/national_instruments/daq_NIDAQmx.py | 3 ++- .../hardware/national_instruments/daq_NIDAQmx_Viewer.py | 7 +++---- .../hardware/national_instruments/daqmxni.py | 3 +-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index 4a32af0..47f6832 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -389,6 +389,7 @@ def commit_settings(self, param: Parameter): def update_task(self): self.channels = self.get_channels_from_settings() + logger.info("update_task - channels: {}".format(self.channels)) self.clock_settings = ClockSettings(frequency=self.settings['clock_settings', 'frequency'], Nsamples=self.settings['clock_settings', 'Nsamples'], edge=Edge.Rising, @@ -399,13 +400,13 @@ def update_task(self): edge=Edge[self.settings['trigger_settings', 'edge']], level=self.settings['trigger_settings', 'level'], ) if not not self.channels: - logger.info("not not self channel") self.controller.update_task(self.channels, self.clock_settings, trigger_settings=self.trigger_settings) def get_channels_from_settings(self): channels = [] if self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(0).name: # analog input for channel in self.settings.child('ai_channels').children(): + logger.info("get_channels_from_settings - channel {}".format(channel)) analog_type = channel['ai_type'] if analog_type == 'Voltage': channels.append(AIChannel(name=channel.opts['title'], diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py index 0deb027..147161b 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py @@ -1,4 +1,4 @@ -import datetime +import nidaqmx import numpy as np import traceback from qtpy import QtCore @@ -16,7 +16,7 @@ class DAQ_NIDAQmx_Viewer(DAQ_Viewer_base, DAQ_NIDAQmx_base): ==================== ======================== **Attributes** **Type** *data_grabed_signal* instance of Signal - *params* dictionnary list + *params* dictionary list *task* ==================== ======================== @@ -61,8 +61,6 @@ def stop(self): logger.info("StopTask() done") except Exception: pass - ############################## - self.emit_status(ThreadCommand('Update_Status', ['Acquisition stopped.'])) return '' @@ -145,6 +143,7 @@ def ini_detector(self, controller=None): DAQ_termination.Auto) info = "Plugin Initialized" initialized = True + logger.info("Detector 0D initialized") return info, initialized except Exception as e: diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index 0093e0a..09ab5d8 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -3,7 +3,6 @@ from enum import IntEnum import numpy as np from pymodaq.utils.logger import set_logger, get_module_name -# from threading import Timer import nidaqmx from nidaqmx.constants import * from nidaqmx.system.device import Device @@ -253,7 +252,7 @@ def __init__(self, **kwargs): class DAQmx: - """Wrapper around the NIDAQmx package giving an easy to use object to instantiate channels and tasks""" + """Wrapper around the NIDAQmx package giving an easy-to-use object to instantiate channels and tasks""" def __init__(self): self.devices = [] self.devices_names = [] From 186b66b4e16846bb0c70a7b75a8216daa2dbbf0d Mon Sep 17 00:00:00 2001 From: Sebastien Date: Mon, 9 Dec 2024 22:22:10 +0100 Subject: [PATCH 28/57] Clarify & optimize code + fix minors problems --- .../plugins_0D/daq_0Dviewer_NIDAQmx.py | 10 +-- .../national_instruments/daq_NIDAQmx.py | 72 ++++++++++--------- .../daq_NIDAQmx_Viewer.py | 9 ++- .../hardware/national_instruments/daqmxni.py | 14 ++-- 4 files changed, 54 insertions(+), 51 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py index fd90a15..32b2033 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py @@ -22,14 +22,15 @@ class DAQ_0DViewer_NIDAQmx(DAQ_NIDAQmx_Viewer): config_modules: list current_device: nidaqmx.system.Device live: bool - data_tot: list Naverage: int - ind_average: int + param_devices = DAQmx.get_NIDAQ_devices().device_names params = viewer_params + [ {'title': 'Display type:', 'name': 'display', 'type': 'list', 'limits': ['0D', '1D']}, - {'title': 'Module ref. :', 'name': 'module', 'type': 'list', 'limits': DAQmx.get_NIDAQ_devices()[1], - 'value': DAQmx.get_NIDAQ_devices()[1][0] + {'title': 'Devices :', 'name': 'devices', 'type': 'list', 'limits': param_devices, + 'value': param_devices[0] + }, + {'title': 'Device To Use:', 'name': 'dev_to_use', 'type': 'list', 'limits': param_devices, }, ] + DAQ_NIDAQmx_base.params @@ -45,7 +46,6 @@ def ini_attributes(self): self.config_modules = [] self.live = False self.Naverage = 1 - self.ind_average = 0 def close(self): self.live = False diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index 47f6832..5d233fa 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -24,8 +24,8 @@ class ScalableGroupAI(GroupParameter): params = [{'title': 'AI type:', 'name': 'ai_type', 'type': 'list', 'limits': DAQ_analog_types.names()}, {'title': 'Voltages:', 'name': 'voltage_settings', 'type': 'group', 'children': [ - {'title': 'Voltage Min:', 'name': 'volt_min', 'type': 'list', 'value': -10.}, - {'title': 'Voltage Max:', 'name': 'volt_max', 'type': 'list', 'value': 10.}, + {'title': 'Voltage Min:', 'name': 'volt_min', 'type': 'float', 'value': -10.}, + {'title': 'Voltage Max:', 'name': 'volt_max', 'type': 'float', 'value': 10.}, ]}, {'title': 'Current:', 'name': 'current_settings', 'type': 'group', 'visible': False, 'children': [ {'title': 'Current Min:', 'name': 'curr_min', 'type': 'float', 'value': -1, 'suffix': 'A'}, @@ -46,7 +46,7 @@ def __init__(self, **opts): opts['addList'] = opts['limits'] GroupParameter.__init__(self, **opts) - def addNew(self, typ): + def addNew(self, typ=None): """ Add a child. @@ -101,7 +101,7 @@ def __init__(self, **opts): opts['addList'] = opts['limits'] GroupParameter.__init__(self, **opts) - def addNew(self, typ): + def addNew(self, typ=None): """ Add a child. @@ -147,7 +147,7 @@ def __init__(self, **opts): opts['addList'] = opts['limits'] GroupParameter.__init__(self, **opts) - def addNew(self, typ): + def addNew(self, typ=None): """ Add a child. @@ -183,7 +183,7 @@ def __init__(self, **opts): opts['addList'] = opts['limits'] GroupParameter.__init__(self, **opts) - def addNew(self, typ): + def addNew(self, typ=None): """ Add a child. @@ -218,7 +218,7 @@ def __init__(self, **opts): opts['addList'] = opts['limits'] GroupParameter.__init__(self, **opts) - def addNew(self, typ): + def addNew(self, typ=None): """ Add a child. @@ -241,9 +241,12 @@ def addNew(self, typ): registerParameterType('groupdo', ScalableGroupDO, override=True) -class DAQ_NIDAQmx_base(DAQmx): +class DAQ_NIDAQmx_base: + """ + Base NIDAQmx class for using DAQmx objects from daqmxni.py in the DAQ_NIDAQmx_Move & DAQ_NIDAQmx_Viewer + """ data_grabed_signal = Signal(list) - + param_instance = DAQmx() params = [{'title': 'Refresh hardware:', 'name': 'refresh_hardware', 'type': 'bool', 'value': False}, {'title': 'Signal type:', 'name': 'NIDAQ_type', 'type': 'list', 'limits': DAQ_NIDAQ_source.names()}, {'title': 'NSamples To Read', 'name': 'nsamplestoread', 'type': 'int', 'value': 1000, 'default': 1000, @@ -327,7 +330,16 @@ def commit_settings(self, param: Parameter): self.settings.child('do_channels').hide() self.settings.child('di_channels').hide() - elif param.value() == DAQ_NIDAQ_source(1).name: # counter input + elif param.value() == DAQ_NIDAQ_source(1).name: # analog output + self.settings.child('clock_settings').show() + self.settings.child('ai_channels').hide() + self.settings.child('ao_channels').show() + self.settings.child('ao_settings').show() + self.settings.child('counter_settings').hide() + self.settings.child('do_channels').hide() + self.settings.child('di_channels').hide() + + elif param.value() == DAQ_NIDAQ_source(2).name: # counter input self.settings.child('clock_settings').hide() self.settings.child('ai_channels').hide() self.settings.child('ao_channels').hide() @@ -336,16 +348,16 @@ def commit_settings(self, param: Parameter): self.settings.child('do_channels').hide() self.settings.child('di_channels').hide() - elif param.value() == DAQ_NIDAQ_source(2).name: # analog output + elif param.value() == DAQ_NIDAQ_source(3).name: # Digital_Input self.settings.child('clock_settings').show() self.settings.child('ai_channels').hide() self.settings.child('ao_channels').show() self.settings.child('ao_settings').show() self.settings.child('counter_settings').hide() self.settings.child('do_channels').hide() - self.settings.child('di_channels').hide() + self.settings.child('di_channels').show() - elif param.value() == DAQ_NIDAQ_source(3).name: # digital output + elif param.value() == DAQ_NIDAQ_source(4).name: # digital output self.settings.child('clock_settings').show() self.settings.child('ai_channels').hide() self.settings.child('ao_channels').hide() @@ -353,15 +365,6 @@ def commit_settings(self, param: Parameter): self.settings.child('counter_settings').hide() self.settings.child('do_channels').show() self.settings.child('di_channels').hide() - - elif param.value() == DAQ_NIDAQ_source(4).name: # Digital_Input - self.settings.child('clock_settings').show() - self.settings.child('ai_channels').hide() - self.settings.child('ao_channels').show() - self.settings.child('ao_settings').show() - self.settings.child('counter_settings').hide() - self.settings.child('do_channels').hide() - self.settings.child('di_channels').show() self.update_task() elif param.name() == 'refresh_hardware': @@ -429,12 +432,7 @@ def get_channels_from_settings(self): thermo_type=DAQ_thermocouples[ channel['thermoc_settings', 'thermoc_type']], )) - elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(1).name: # counter input - for channel in self.settings.child('counter_settings', 'counter_channels').children(): - channels.append(Counter(name=channel.opts['title'], - source='Counter', edge=channel['edge'])) - - elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(2).name: # analog output + elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(1).name: # analog output for channel in self.settings.child('ao_channels').children(): analog_type = channel['ao_type'] channels.append(AOChannel(name=channel.opts['title'], @@ -442,14 +440,22 @@ def get_channels_from_settings(self): value_min=channel['voltage_settings', 'volt_min'], value_max=channel['voltage_settings', 'volt_max'], )) - elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(3).name: # Digital output - for channel in self.settings.child('do_channels').children(): - channels.append(DOChannel(name=channel.opts['title'], - source='Digital_Output')) - elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(4).name: # digital input + + elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(2).name: # counter input + for channel in self.settings.child('counter_settings', 'counter_channels').children(): + channels.append(Counter(name=channel.opts['title'], + source='Counter', edge=channel['edge'])) + + elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(3).name: # digital input for channel in self.settings.child('di_channels').children(): channels.append(DIChannel(name=channel.opts['title'], source='Digital_Input')) + + elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(4).name: # Digital output + for channel in self.settings.child('do_channels').children(): + channels.append(DOChannel(name=channel.opts['title'], + source='Digital_Output')) + channels = [ch for ch in channels if self.settings.child("dev_to_use").value() in ch.name] return channels def stop(self): diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py index 147161b..0069ae2 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py @@ -54,11 +54,10 @@ def __init__(self, parent=None, params_state=None, control_type="0D"): def stop(self): """Stop the current grab hardware wise if necessary""" - self.controller.stop() - self.live = False try: - self.controller.task.StopTask() - logger.info("StopTask() done") + self.controller.stop() + self.live = False + logger.info("Acquisition stopped.") except Exception: pass self.emit_status(ThreadCommand('Update_Status', ['Acquisition stopped.'])) @@ -81,7 +80,7 @@ def commit_settings(self, param): if param.parent() is not None: if param.parent().name() == 'ai_channels': device = param.opts['title'].split('/')[0] - self.settings.child('clock_settings', 'frequency').setOpts(max=self.getAIMaxRate(device)) + self.settings.child('clock_settings', 'frequency').setOpts(max=self.controller.getAIMaxRate(device)) ranges = self.controller.getAIVoltageRange(device) param.child('voltage_settings', 'volt_min').setOpts(limits=[r[0] for r in ranges]) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index 09ab5d8..c77577c 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -255,7 +255,6 @@ class DAQmx: """Wrapper around the NIDAQmx package giving an easy-to-use object to instantiate channels and tasks""" def __init__(self): self.devices = [] - self.devices_names = [] self.channels = [] self._device = None self._task = None @@ -276,13 +275,12 @@ def device(self): @device.setter def device(self, device): - if device not in self.devices_names: + if device not in self.devices.device_names: raise IOError(f'your device: {device} is not known or connected') self._device = device def update_NIDAQ_devices(self): - self.devices = self.get_NIDAQ_devices()[0] - self.devices_names = self.get_NIDAQ_devices()[1] + self.devices = self.get_NIDAQ_devices() @classmethod def get_NIDAQ_devices(cls): @@ -297,12 +295,12 @@ def get_NIDAQ_devices(cls): devices = nidaqmx.system.System.local().devices if devices == ['']: devices = [] - return devices, devices.device_names + return devices except DaqError as e: return e.error_code def update_NIDAQ_channels(self, source_type=None): - self.channels = self.get_NIDAQ_channels(self.devices_names, source_type=source_type) + self.channels = self.get_NIDAQ_channels(self.devices.device_names, source_type=source_type) @classmethod def get_NIDAQ_channels(cls, devices=None, source_type=None): @@ -321,7 +319,7 @@ def get_NIDAQ_channels(cls, devices=None, source_type=None): """ if devices is None: - devices = cls.get_NIDAQ_devices()[1] + devices = cls.get_NIDAQ_devices().device_names if source_type is None: source_type = DAQ_NIDAQ_source.names() @@ -455,7 +453,7 @@ def isDigitalTriggeringSupported(cls, device): def getTriggeringSources(cls, devices=None): sources = [] if devices is None: - devices = cls.get_NIDAQ_devices()[1] + devices = cls.get_NIDAQ_devices().device_names for device in devices: if cls.isDigitalTriggeringSupported(device): From 6782d284f46e33b59df5ea884d0a182450c1ede1 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Mon, 9 Dec 2024 22:35:30 +0100 Subject: [PATCH 29/57] Update README --- README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index d2ae413..6839891 100644 --- a/README.rst +++ b/README.rst @@ -26,6 +26,7 @@ Contributors * Amelie Jarnac * Aurore Finco +* Sébastien Guerrero (sebastien.guerrero@insa-lyon.fr) Instruments =========== @@ -40,6 +41,6 @@ Viewer0D ++++++++ * **DAQmx_PLcounter**: Single photon counting - +* **NIDAQmx: Analog Input (current-voltage-temperature), working with cDAQ & DAQ-USB From 46cf0ca56f0871903b1ae9ac2c91c6ea01bf446c Mon Sep 17 00:00:00 2001 From: Loic Guilmard Date: Wed, 18 Dec 2024 17:09:06 +0100 Subject: [PATCH 30/57] removed CustomEnum to use the ones from nidaq.constants --- .../plugins_0D/daq_0Dviewer_DAQmx.py | 5 +- .../plugins_0D/daq_0Dviewer_NIDAQmx.py | 36 +- .../national_instruments/daq_NIDAQmx.py | 428 +++++++++++++++++- .../hardware/national_instruments/daqmxni.py | 190 +++----- 4 files changed, 497 insertions(+), 162 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx.py index 87be685..e8dba9f 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx.py @@ -1,5 +1,6 @@ -from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx_Viewer import DAQ_NIDAQmx_Viewer -from pymodaq.control_modules.viewer_utility_classes import main +from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_Viewer +from pymodaq.control_modules.viewer_utility_classes import main + class DAQ_0DViewer_DAQmx(DAQ_NIDAQmx_Viewer): diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py index 32b2033..10dbd42 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py @@ -1,8 +1,10 @@ from pymodaq.control_modules.viewer_utility_classes import main from pymodaq.control_modules.viewer_utility_classes import comon_parameters as viewer_params from pymodaq_plugins_daqmx import config -from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import AIChannel, AIThermoChannel, DAQmx, nidaqmx,\ - DAQ_termination, DAQ_thermocouples +from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import AIChannel, AIThermoChannel, DAQmx, \ + Task, niconstants +from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import UsageTypeAI, ThermocoupleType, \ + TerminalConfiguration from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_base from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx_Viewer import DAQ_NIDAQmx_Viewer from pymodaq.utils.logger import set_logger, get_module_name @@ -62,12 +64,10 @@ def close(self): else: try: print("In main") - import nidaqmx as ni - from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import CurrentUnits, TemperatureUnits,\ - VoltageUnits, CJCSource + import nidaqmx.system.System # EXPLORE DEVICES - devices = ni.system.System.local().devices + devices = nidaqmx.system.System.local().devices print("devices {}".format(devices)) print("devices names {}".format(devices.device_names)) print("devices types {}".format([dev.product_type for dev in devices])) @@ -94,26 +94,26 @@ def close(self): analog_type='Thermocouple', value_min=-100, value_max=1000, - thermo_type=DAQ_thermocouples.K), + thermo_type=ThermocoupleType.K), ] channels_voltage = [AIChannel(name="cDAQ1Mod3/ai0", source='Analog_Input', analog_type='voltage', value_min=-80.0e-3, value_max=80.0e-3, - termination=DAQ_termination.Auto, + termination=TerminalConfiguration.DEFAULT, ), AIChannel(name="cDAQ1Mod3/ai1", source='Analog_Input', analog_type='voltage', value_min=-80.0e-3, value_max=80.0e-3, - termination=DAQ_termination.Auto, + termination=TerminalConfiguration.DEFAULT, ), ] # CREATE TASK - task_9211 = nidaqmx.Task() - task_9205 = nidaqmx.Task() + task_9211 = Task() + task_9205 = Task() def callback_9211(task_handle, every_n_samples_event_type, number_of_samples, callback_data): data9211 = task_9211.read(5) @@ -128,9 +128,9 @@ def callback_9205(task_handle, every_n_samples_event_type, number_of_samples, ca "", channel.value_min, channel.value_max, - TemperatureUnits.DEG_C, + niconstants.TemperatureUnits.DEG_C, channel.thermo_type, - CJCSource.BUILT_IN, + niconstants.CJCSource.BUILT_IN, 0., "") for channel in channels_voltage: @@ -139,14 +139,14 @@ def callback_9205(task_handle, every_n_samples_event_type, number_of_samples, ca channel.termination, channel.value_min, channel.value_max, - VoltageUnits.VOLTS, + niconstants.VoltageUnits.VOLTS, "") - task_9211.timing.cfg_samp_clk_timing(5.0, None, nidaqmx.constants.Edge.RISING, - nidaqmx.constants.AcquisitionType.CONTINUOUS, 5) + task_9211.timing.cfg_samp_clk_timing(5.0, None, niconstants.Edge.RISING, + niconstants.AcquisitionType.CONTINUOUS, 5) task_9211.register_every_n_samples_acquired_into_buffer_event(10, callback_9211) - task_9205.timing.cfg_samp_clk_timing(10, None, nidaqmx.constants.Edge.RISING, - nidaqmx.constants.AcquisitionType.CONTINUOUS, 10) + task_9205.timing.cfg_samp_clk_timing(10, None, niconstants.Edge.RISING, + niconstants.AcquisitionType.CONTINUOUS, 10) task_9205.register_every_n_samples_acquired_into_buffer_event(2, callback_9205) task_9211.start() diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index 5d233fa..eebfee0 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -1,14 +1,23 @@ from qtpy import QtWidgets -from qtpy.QtCore import Signal +from qtpy.QtCore import Signal, QTimer +import numpy as np from pymodaq.utils.logger import set_logger, get_module_name +from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters as viewer_params +from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, comon_parameters as actuator_params +import traceback from pymodaq.utils.parameter import Parameter from pymodaq.utils.parameter.pymodaq_ptypes import registerParameterType, GroupParameter -from .daqmxni import DAQmx, DAQ_analog_types, DAQ_thermocouples, DAQ_termination, Edge, DAQ_NIDAQ_source, \ +from .daqmxni import DAQmx, Edge, DAQ_NIDAQ_source, \ ClockSettings, AIChannel, Counter, AIThermoChannel, AOChannel, TriggerSettings, DOChannel, DIChannel logger = set_logger(get_module_name(__file__)) +from .daqmxni import UsageTypeAI, ThermocoupleType, TerminalConfiguration +from pymodaq.utils.daq_utils import ThreadCommand, getLineInfo +from pymodaq.utils.data import DataToExport, DataFromPlugins, DataActuator +from easydict import EasyDict as edict class ScalableGroupAI(GroupParameter): + """ | @@ -22,7 +31,7 @@ class ScalableGroupAI(GroupParameter): hardware.DAQ_Move_Stage_type """ - params = [{'title': 'AI type:', 'name': 'ai_type', 'type': 'list', 'limits': DAQ_analog_types.names()}, + params = [{'title': 'AI type:', 'name': 'ai_type', 'type': 'list', 'limits': UsageTypeAI._member_names_}, {'title': 'Voltages:', 'name': 'voltage_settings', 'type': 'group', 'children': [ {'title': 'Voltage Min:', 'name': 'volt_min', 'type': 'float', 'value': -10.}, {'title': 'Voltage Max:', 'name': 'volt_max', 'type': 'float', 'value': 10.}, @@ -32,12 +41,12 @@ class ScalableGroupAI(GroupParameter): {'title': 'Current Max:', 'name': 'curr_max', 'type': 'float', 'value': 1, 'suffix': 'A'}, ]}, {'title': 'Thermocouple:', 'name': 'thermoc_settings', 'type': 'group', 'visible': False, 'children': [ - {'title': 'Thc. type:', 'name': 'thermoc_type', 'type': 'list', 'limits': DAQ_thermocouples.names(), + {'title': 'Thc. type:', 'name': 'thermoc_type', 'type': 'list', 'limits': ThermocoupleType._member_names_, 'value': 'K'}, {'title': 'Temp. Min (°C):', 'name': 'T_min', 'type': 'float', 'value': 0, 'suffix': '°C'}, {'title': 'Temp. Max (°C):', 'name': 'T_max', 'type': 'float', 'value': 50, 'suffix': '°C'}, ]}, - {'title': 'Termination:', 'name': 'termination', 'type': 'list', 'limits': DAQ_termination.names()}, + {'title': 'Termination:', 'name': 'termination', 'type': 'list', 'limits': TerminalConfiguration._member_names_}, ] def __init__(self, **opts): @@ -84,7 +93,7 @@ class ScalableGroupAO(GroupParameter): hardware.DAQ_Move_Stage_type """ - params = [{'title': 'AO type:', 'name': 'ao_type', 'type': 'list', 'limits': DAQ_analog_types.names()[0:2]}, + params = [{'title': 'AO type:', 'name': 'ao_type', 'type': 'list', 'limits': UsageTypeAI._member_names_[0:2]}, {'title': 'Voltages:', 'name': 'voltage_settings', 'type': 'group', 'children': [ {'title': 'Voltage Min:', 'name': 'volt_min', 'type': 'list', 'value': -10., }, {'title': 'Voltage Max:', 'name': 'volt_max', 'type': 'list', 'value': 10., }, @@ -395,7 +404,7 @@ def update_task(self): logger.info("update_task - channels: {}".format(self.channels)) self.clock_settings = ClockSettings(frequency=self.settings['clock_settings', 'frequency'], Nsamples=self.settings['clock_settings', 'Nsamples'], - edge=Edge.Rising, + edge=Edge.RISING, repetition=self.live, ) self.trigger_settings = \ TriggerSettings(trig_source=self.settings['trigger_settings', 'trigger_channel'], @@ -416,20 +425,20 @@ def get_channels_from_settings(self): source='Analog_Input', analog_type=analog_type, value_min=channel['voltage_settings', 'volt_min'], value_max=channel['voltage_settings', 'volt_max'], - termination=DAQ_termination[channel['termination']], )) + termination=TerminalConfiguration[channel['termination']], )) elif analog_type == 'Current': channels.append(AIChannel(name=channel.opts['title'], source='Analog_Input', analog_type=analog_type, value_min=channel['current_settings', 'curr_min'], value_max=channel['current_settings', 'curr_max'], - termination=DAQ_termination[channel['termination']], )) + termination=TerminalConfiguration[channel['termination']], )) elif analog_type == 'Thermocouple': channels.append(AIThermoChannel(name=channel.opts['title'], source='Analog_Input', analog_type=analog_type, value_min=channel['thermoc_settings', 'T_min'], value_max=channel['thermoc_settings', 'T_max'], termination=channel['termination'], - thermo_type=DAQ_thermocouples[ + thermo_type=ThermocoupleType[ channel['thermoc_settings', 'thermoc_type']], )) elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(1).name: # analog output @@ -465,3 +474,402 @@ def stop(self): self.timer.stop() QtWidgets.QApplication.processEvents() self.controller.stop() + + +class DAQ_NIDAQmx_Viewer(DAQ_Viewer_base, DAQ_NIDAQmx_base): + """ + ==================== ======================== + **Attributes** **Type** + *data_grabed_signal* instance of Signal + *params* dictionnary list + *task* + ==================== ======================== + + See Also + -------- + refresh_hardware + """ + + live_mode_available = True + params = viewer_params + DAQ_NIDAQmx_base.params + + def __init__(self, parent=None, params_state=None, control_type="0D"): + DAQ_Viewer_base.__init__(self, parent, params_state) # defines settings attribute and various other methods + DAQ_NIDAQmx_base.__init__(self) + + self.Naverage = None + self.live = False + self.control_type = control_type # could be "0D", "1D" or "Actuator" + if self.control_type == "0D": + self.settings.child('NIDAQ_type').setLimits( + ['Analog_Input', 'Counter', 'Digital_Input']) # analog input and counter + elif self.control_type == "1D": + self.settings.child('NIDAQ_type').setLimits(['Analog_Input']) + elif self.control_type == "Actuator": + self.settings.child('NIDAQ_type').setLimits(['Analog_Output']) + + self.settings.child('ao_settings').hide() + self.settings.child('ao_channels').hide() + + # timer used for the counter + self.timer = QTimer() + self.timer.setSingleShot(True) + self.timer.timeout.connect(self.counter_done) + + def stop(self): + """Stop the current grab hardware wise if necessary""" + self.controller.stop() + self.live = False + self.emit_status(ThreadCommand('Update_Status', ['Acquisition stopped.'])) + return '' + + def commit_settings(self, param): + """ + Activate the parameters changes in the hardware. + + =============== ================================ =========================== + **Parameters** **Type** **Description** + *param* instance of pyqtgraph.parameter the parameter to activate + =============== ================================ =========================== + + See Also + -------- + update_NIDAQ_channels, update_task, DAQ_NIDAQ_source, refresh_hardware + """ + + if param.parent() is not None: + if param.parent().name() == 'ai_channels': + device = param.opts['title'].split('/')[0] + self.settings.child('clock_settings', 'frequency').setOpts(max=self.getAIMaxRate(device)) + + ranges = self.controller.getAIVoltageRange(device) + param.child('voltage_settings', 'volt_min').setOpts(limits=[r[0] for r in ranges]) + param.child('voltage_settings', 'volt_max').setOpts(limits=[r[1] for r in ranges]) + + DAQ_NIDAQmx_base.commit_settings(self, param) + + def ini_detector(self, controller=None): + """ + Initialisation procedure of the detector. + + See Also + -------- + daq_utils.ThreadCommand + """ + try: + self.controller = self.ini_detector_init(controller, DAQmx()) + self.update_task() + + # actions to perform in order to set properly the settings tree options + self.commit_settings(self.settings.child('NIDAQ_type')) + + info = "Plugin Initialized" + initialized = True + return info, initialized + + except Exception as e: + logger.info(traceback.format_exc()) + self.emit_status(ThreadCommand('Update_Status', [str(e), 'log'])) + info = str(e) + initialized = False + return info, initialized + + def grab_data(self, Naverage=1, **kwargs): + """ + | grab the current values with NIDAQ profile procedure. + | + | Send the data_grabed_signal once done. + + =============== ======== =============================================== + **Parameters** **Type** **Description** + *Naverage* int Number of values to average + =============== ======== =============================================== + + See Also + -------- + DAQ_NIDAQ_source + """ + update = False + + if 'live' in kwargs: + if kwargs['live'] != self.live: + update = True + self.live = kwargs['live'] + + if Naverage != self.Naverage: + self.Naverage = Naverage + update = True + if update: + self.update_task() + + if self.controller.task is None: + self.update_task() + + self.controller.register_callback(self.emit_data, "Nsamples", self.clock_settings.Nsamples) + self.controller.start() + + def emit_data(self, task_handle, every_n_samples_event_type, number_of_samples, callback_data): + channels_names = [ch.name for ch in self.channels] + # channels_ai_names = [ch.name for ch in self.channels if ch.source == 'Analog_Input'] + data_from_task = self.controller.task.read(self.settings['nsamplestoread'], timeout=20.0) + if self.control_type == "0D": + if not len(self.controller.task.channels.channel_names) != 1: + data_dfp = [np.array(data_from_task)] + else: + data_dfp = list(map(np.array, data_from_task)) + dte = DataToExport(name='NIDAQmx', + data=[DataFromPlugins(name='NI Analog Input', + data=data_dfp, + dim=f'Data{self.settings.child("display").value()}', + labels=channels_names + ), + ]) + self.dte_signal.emit(dte) + return 0 # mandatory for the NIDAQmx callback + + def counter_done(self): + channels_name = [ch.name for ch in self.channels] + data_counter = self.readCounter(len(self.channels), + self.settings['counter_settings', 'counting_time'] * 1e-3) + self.data_grabed_signal.emit([DataFromPlugins(name='NI Counter', data=[data_counter / 1e-3], dim='Data0D', + labels=channels_name, )]) + # y_axis=Axis(label='Count Number', units='1/s'))]) + self.task.StopTask() + +class DAQ_NIDAQmx_Actuator(DAQ_Move_base, DAQ_NIDAQmx_base): + """ + Wrapper object to access the Mock fonctionnalities, similar wrapper for all controllers. + + =============== ============== + **Attributes** **Type** + *params* dictionnary + =============== ============== + """ + _controller_units = 'Volts' + is_multiaxes = False # set to True if this plugin is controlled for a multiaxis controller (with a unique communication link) + stage_names = [] # "list of strings of the multiaxes + + params = DAQ_NIDAQmx_base.params + [ + # elements to be added here as dicts in order to control your custom stage + ############ + {'title': 'MultiAxes:', 'name': 'multiaxes', 'type': 'group', 'visible': is_multiaxes, + 'children': [ + {'title': 'is Multiaxes:', 'name': 'ismultiaxes', 'type': 'bool', 'value': is_multiaxes, + 'default': False}, + {'title': 'Status:', 'name': 'multi_status', 'type': 'list', 'value': 'Master', + 'limits': ['Master', 'Slave']}, + {'title': 'Axis:', 'name': 'axis', 'type': 'list', 'limits': stage_names}, + ]}] + actuator_params + + def __init__(self, parent=None, params_state=None, control_type="Actuator"): + DAQ_Move_base.__init__(self, parent, params_state) # defines settings attribute and various other methods + DAQ_NIDAQmx_base.__init__(self) + + self.control_type = "Actuator" # could be "0D", "1D" or "Actuator" + self.settings.child('NIDAQ_type').setLimits(['Analog_Output', 'Digital_Output']) + + self.settings.child('clock_settings', 'Nsamples').setValue(1) + + def get_actuator_value(self) -> DataActuator: + """Get the current position from the hardware with scaling conversion. + + Returns + ------- + float: The position obtained after scaling conversion. + """ + + pos = self.target_position + ## + + pos = self.get_position_with_scaling(pos) + self.emit_status(ThreadCommand('check_position', [pos])) + return pos + + def commit_settings(self, param): + """ + | Activate any parameter changes on the PI_GCS2 hardware. + | + | Called after a param_tree_changed signal from DAQ_Move_main. + + """ + + DAQ_NIDAQmx_base.commit_settings(self, param) + if param.name() == 'waveform': + if param.value() == 'DC': + self.settings.child('ao_settings', 'cont_param').setValue('offset') + self.settings.child('ao_settings', 'cont_param').show(not param.value() == 'DC') + self.settings.child('ao_settings', 'waveform_settings').show(not param.value() == 'DC') + + if param.parent() is not None: + if param.parent().name() == 'ao_channels': + device = param.opts['title'].split('/')[0] + self.settings.child('clock_settings', 'frequency').setOpts(max=self.getAOMaxRate(device)) + + ranges = self.getAOVoltageRange(device) + param.child('voltage_settings', 'volt_min').setOpts(limits=[r[0] for r in ranges]) + param.child('voltage_settings', 'volt_max').setOpts(limits=[r[1] for r in ranges]) + + def ini_stage(self, controller=None): + """Actuator communication initialization + + Parameters + ---------- + controller: (object) custom object of a PyMoDAQ plugin (Slave case). None if only one actuator by controller (Master case) + + Returns + ------- + self.status (edict): with initialization status: three fields: + * info (str) + * controller (object) initialized controller + *initialized: (bool): False if initialization failed otherwise True + """ + + try: + # initialize the stage and its controller status + # controller is an object that may be passed to other instances of DAQ_Move_Mock in case + # of one controller controlling multiactuators (or detector) + + self.status.update(edict(info="", controller=None, initialized=False)) + + # check whether this stage is controlled by a multiaxe controller (to be defined for each plugin) + # if multiaxes then init the controller here if Master state otherwise use external controller + if self.settings['multiaxes', 'ismultiaxes'] and self.settings['multiaxes', + 'multi_status'] == "Slave": + if controller is None: + raise Exception('no controller has been defined externally while this axe is a slave one') + else: + self.controller = controller + else: + self.controller = 'A Nidaqmx task' + self.update_task() + + # actions to perform in order to set properly the settings tree options + self.commit_settings(self.settings.child('NIDAQ_type')) + + self.status.info = "Plugin Initialized" + self.status.controller = self.controller + self.status.initialized = True + return self.status + + except Exception as e: + self.emit_status(ThreadCommand('Update_Status', [getLineInfo() + str(e), 'log'])) + self.status.info = getLineInfo() + str(e) + self.status.initialized = False + return self.status + + def calulate_waveform(self, value): + waveform = self.settings['ao_settings', 'waveform'] + if waveform == 'DC': + values = np.array([value]) + else: + Nsamples = self.settings['clock_settings', 'Nsamples'] + freq = self.settings['clock_settings', 'frequency'] + time = np.linspace(0, Nsamples / freq, Nsamples, endpoint=False) + + freq0 = self.settings['ao_settings', 'waveform_settings', 'frequency'] + amp = self.settings['ao_settings', 'waveform_settings', 'amplitude'] + offset = self.settings['ao_settings', 'waveform_settings', 'offset'] + if waveform == 'Sinus': + values = offset + amp * np.sin(2 * np.pi * freq0 * time) + elif waveform == 'Ramp': + values = offset + amp * np.linspace(0, 1, Nsamples) + + return values + + def move_Abs(self, position): + """ Move the actuator to the absolute target defined by position + + Parameters + ---------- + position: (flaot) value of the absolute target positioning + """ + + position = self.check_bound(position) # if user checked bounds, the defined bounds are applied here + position = self.set_position_with_scaling(position) # apply scaling if the user specified one + if self.settings['NIDAQ_type'] == 'Analog_Output': + self.settings.child('ao_settings', 'waveform_settings', + self.settings['ao_settings', 'cont_param']).setValue(position) + values = self.calulate_waveform(position) + self.target_position = position + + self.stop() + + if len(values) == 1: + self.writeAnalog(len(values), 1, values, autostart=True) + self.current_position = self.check_position() + self.move_done() + else: + if self.c_callback is None: + self.register_callback(self.move_done_callback) + self.writeAnalog(len(values), 1, values, autostart=False) + self.task.StartTask() + elif self.settings['NIDAQ_type'] == 'Digital_Output': + self.writeDigital(1, np.array([position], dtype=np.uint8), autostart=True) + + def move_done_callback(self, taskhandle, status, callbackdata): + self.current_position = self.check_position() + QtWidgets.QApplication.processEvents() + self.move_done() + self.task.StopTask() + return 0 + + def move_Rel(self, position): + """ Move the actuator to the relative target actuator value defined by position + + Parameters + ---------- + position: (flaot) value of the relative target positioning + """ + + position = self.check_bound(self.current_position + position) - self.current_position + self.target_position = position + self.current_position + if self.settings['NIDAQ_type'] == 'Analog_Output': + self.settings.child('ao_settings', 'waveform_settings', + self.settings['ao_settings', 'cont_param']).setValue(self.target_position) + + values = self.calulate_waveform(self.target_position) + + self.stop() + + if len(values) == 1: + self.writeAnalog(len(values), 1, values, autostart=True) + self.current_position = self.check_position() + self.move_done() + else: + if self.c_callback is None: + self.register_callback(self.move_done_callback) + self.writeAnalog(len(values), 1, values, autostart=False) + self.task.StartTask() + elif self.settings['NIDAQ_type'] == 'Digital_Output': + self.writeDigital(1, np.array([self.target_position], dtype=np.uint8), autostart=True) + + def move_Home(self): + """ + Send the update status thread command. + See Also + -------- + daq_utils.ThreadCommand + """ + + self.stop() + + if self.c_callback is None: + self.register_callback(self.move_done_callback) + self.writeAnalog(1, 1, np.array([0.])) + self.task.StartTask() + + def stop_motion(self): + """ + Call the specific move_done function (depending on the hardware). + + See Also + -------- + move_done + """ + + ## TODO for your custom plugin + self.stop() + self.emit_status(ThreadCommand('Update_Status', ['Some info you want to log'])) + self.move_done() # to let the interface know the actuator stopped + ############################## +>>>>>>> eda1396 (removed CustomEnum to use the ones from nidaq.constants) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index c77577c..19aac63 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -3,9 +3,14 @@ from enum import IntEnum import numpy as np from pymodaq.utils.logger import set_logger, get_module_name -import nidaqmx -from nidaqmx.constants import * + +from nidaqmx.constants import UsageTypeAI, ThermocoupleType, TerminalConfiguration, Edge, AcquisitionType, \ + VoltageUnits, CurrentUnits, CurrentShuntResistorLocation, TemperatureUnits, \ + CJCSource, CountDirection, Level, FrequencyUnits, TimeUnits, LineGrouping +from nidaqmx.system import System + from nidaqmx.system.device import Device +from nidaqmx import Task from nidaqmx.errors import DaqError, DAQmxErrors from pymodaq_plugins_daqmx import config @@ -13,134 +18,55 @@ logger = set_logger(get_module_name(__file__)) -class DAQ_NIDAQ_source(IntEnum): - """ - Enum class of NIDAQ_source - - =============== ========== - **Attributes** **Type** - *Analog_Input* int - *Counter* int - =============== ========== - """ - Analog_Input = 0 - Analog_Output = 1 - Counter = 2 - Digital_Input = 3 - Digital_Output = 4 - Terminals = 5 - - @classmethod - def names(cls): - return [name for name, member in cls.__members__.items()] - - @classmethod - def members(cls): - return [member for name, member in cls.__members__.items()] - - -class DAQ_analog_types(IntEnum): +class IntEnumExtend(IntEnum): """ - Enum class of Ai types - - =============== ========== - **Attributes** **Type** - =============== ========== + This class expose 3 privates and undocumented methods of Enum """ - Voltage = UsageTypeAI.VOLTAGE.value - Current = UsageTypeAI.CURRENT.value - Thermocouple = UsageTypeAI.TEMPERATURE_THERMOCOUPLE.value - @classmethod def names(cls): - return [name for name, member in cls.__members__.items()] + return cls._member_names_ + # [item.name for item in cls.__members__.items()] + # [name for name, member in cls.__members__.items()] @classmethod def members(cls): - return [member for name, member in cls.__members__.items()] + return list(cls._member_map_.values()) + # [member for name, member in cls.__members__.items()] + # [item.member for item in cls.__members__.items()] @classmethod def values(cls): - return [cls[name].value for name, member in cls.__members__.items()] - - -class DAQ_thermocouples(IntEnum): - """ - Enum class of thermocouples type - - =============== ========== - **Attributes** **Type** - =============== ========== - """ - J = ThermocoupleType.J.value - K = ThermocoupleType.K.value - N = ThermocoupleType.N.value - R = ThermocoupleType.R.value - S = ThermocoupleType.S.value - T = ThermocoupleType.T.value - B = ThermocoupleType.B.value - E = ThermocoupleType.E.value - - @classmethod - def names(cls): - return [name for name, member in cls.__members__.items()] + return [cls[name].value for name in cls._member_names_] + # [cls[name].value for name, member in cls.__members__.items()] - @classmethod - def members(cls): - return [member for name, member in cls.__members__.items()] - -class DAQ_termination(IntEnum): +class DAQ_NIDAQ_source(IntEnumExtend): """ - Enum class of termination type + Enum class of NIDAQ_source =============== ========== **Attributes** **Type** + *Analog_Input* int + *Counter* int =============== ========== """ - Auto = TerminalConfiguration.DEFAULT.value - RSE = TerminalConfiguration.RSE.value - NRSE = TerminalConfiguration.NRSE.value - Diff = TerminalConfiguration.DIFF.value - Pseudodiff = TerminalConfiguration.PSEUDO_DIFF.value - - @classmethod - def names(cls): - return [name for name, member in cls.__members__.items()] - - @classmethod - def members(cls): - return [member for name, member in cls.__members__.items()] - - -class Edge(IntEnum): - """ - """ - Rising = Edge.RISING.value - Falling = Edge.FALLING.value - - @classmethod - def names(cls): - return [name for name, member in cls.__members__.items()] - - @classmethod - def members(cls): - return [member for name, member in cls.__members__.items()] - - -class ClockMode(IntEnum): - """ - """ - Finite = AcquisitionType.FINITE.value - Continuous = AcquisitionType.CONTINUOUS.value + Analog_Input = 0 + Analog_Output = 1 + Counter = 2 + Digital_Input = 3 + Digital_Output = 4 + Terminals = 5 - @classmethod - def names(cls): - return [name for name, member in cls.__members__.items()] - @classmethod - def members(cls): - return [member for name, member in cls.__members__.items()] +# class DAQ_analog_types(UsageTypeAI): +# """ +# Enum class of Ai types +# +# =============== ========== +# **Attributes** **Type** +# =============== ========== +# """ +# Thermocouple = UsageTypeAI.TEMPERATURE_THERMOCOUPLE.value class ClockSettingsBase: @@ -151,7 +77,7 @@ def __init__(self, Nsamples=1000, repetition=False): class ClockSettings(ClockSettingsBase): - def __init__(self, source=None, frequency=1000, Nsamples=1000, edge=Edge.Rising, repetition=False): + def __init__(self, source=None, frequency=1000, Nsamples=1000, edge=Edge.RISING, repetition=False): super().__init__(Nsamples, repetition) self.source = source assert edge in Edge.members() @@ -168,7 +94,7 @@ def __init__(self, Nsamples=1000, rising_channel='', falling_channel='', class TriggerSettings: - def __init__(self, trig_source='', enable=False, edge=Edge.Rising, level=0.1): + def __init__(self, trig_source='', enable=False, edge=Edge.RISING, level=0.1): assert edge in Edge.members() self.trig_source = trig_source self.enable = enable @@ -189,7 +115,7 @@ def __init__(self, name='', source=DAQ_NIDAQ_source.Analog_Input): class AChannel(Channel): - def __init__(self, analog_type=DAQ_analog_types.Voltage, value_min=-10., value_max=+10., **kwargs): + def __init__(self, analog_type=UsageTypeAI.VOLTAGE, value_min=-10., value_max=+10., **kwargs): """ Parameters ---------- @@ -203,16 +129,16 @@ def __init__(self, analog_type=DAQ_analog_types.Voltage, value_min=-10., value_m class AIChannel(AChannel): - def __init__(self, termination=DAQ_termination.Auto, **kwargs): + def __init__(self, termination=TerminalConfiguration.DEFAULT, **kwargs): super().__init__(**kwargs) - assert termination in DAQ_termination.members() + assert termination in TerminalConfiguration._member_map_.values() self.termination = termination class AIThermoChannel(AIChannel): - def __init__(self, thermo_type=DAQ_thermocouples.K, **kwargs): + def __init__(self, thermo_type=ThermocoupleType.K, **kwargs): super().__init__(**kwargs) - assert thermo_type in DAQ_thermocouples.members() + assert thermo_type in ThermocoupleType._member_map_.values() self.thermo_type = thermo_type @@ -222,8 +148,8 @@ def __init__(self, **kwargs): class Counter(Channel): - def __init__(self, edge=Edge.Rising, **kwargs): - assert edge in Edge.members() + def __init__(self, edge=Edge.RISING, **kwargs): + assert edge in Edge._member_map_.values() super().__init__(**kwargs) self.edge = edge self.counter_type = "Edge Counter" @@ -292,7 +218,7 @@ def get_NIDAQ_devices(cls): list of devices as strings to be used in subsequent commands """ try: - devices = nidaqmx.system.System.local().devices + devices = System.local().devices if devices == ['']: devices = [] return devices @@ -330,17 +256,17 @@ def get_NIDAQ_channels(cls, devices=None, source_type=None): if not not devices: for device in devices: for source in source_type: - if source == DAQ_NIDAQ_source['Analog_Input'].name: # analog input + if source == DAQ_NIDAQ_source.Analog_Input.name: # analog input channels = Device(device).ai_physical_chans.channel_names - elif source == DAQ_NIDAQ_source['Analog_Output'].name: # analog output + elif source == DAQ_NIDAQ_source.Analog_Output.name: # analog output channels = Device(device).ao_physical_chans.channel_names - elif source == DAQ_NIDAQ_source['Counter'].name: # counter + elif source == DAQ_NIDAQ_source.Counter.name: # counter channels = Device(device).ci_physical_chans.channel_names - elif source == DAQ_NIDAQ_source['Digital_Output'].name: # digital output + elif source == DAQ_NIDAQ_source.Digital_Output.name: # digital output channels = Device(device).do_lines.channel_names - elif source == DAQ_NIDAQ_source['Digital_Input'].name: # digital iutput + elif source == DAQ_NIDAQ_source.Digital_Input.name: # digital iutput channels = Device(device).di_lines.channel_names - elif source == DAQ_NIDAQ_source['Terminals'].name: # terminals + elif source == DAQ_NIDAQ_source.Terminals.name: # terminals channels = Device(device).terminals if channels != ['']: @@ -471,13 +397,13 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti try: if self._task is not None: - if isinstance(self._task, nidaqmx.Task): + if isinstance(self._task, Task): self._task.close() self._task = None self.c_callback = None - self._task = nidaqmx.Task() + self._task = Task() logger.info("TASK: {}".format(self._task)) err_code = None @@ -662,12 +588,12 @@ def readCounter(self): @classmethod def getAIVoltageRange(cls, device='Dev1'): - ret = nidaqmx.system.System.local().devices[device].ai_voltage_rngs + ret = System.local().devices[device].ai_voltage_rngs # todo self.devices[device].ai_voltage_rngs return [tuple(ret[6:8])] @classmethod def getAOVoltageRange(cls, device='Dev1'): - ret = nidaqmx.system.System.local().devices[device].ao_voltage_rngs + ret = System.local().devices[device].ao_voltage_rngs # todo self.devices[device].ao_voltage_rngs return [tuple(ret)] # [(-10., 10.)] Why this format is needed?? def stop(self): @@ -710,8 +636,8 @@ def refresh_hardware(self): -------- update_NIDAQ_devices, update_NIDAQ_channels """ - devices = self.update_NIDAQ_devices() - self.update_NIDAQ_channels(devices) + self.update_NIDAQ_devices() + self.update_NIDAQ_channels() if __name__ == '__main__': From 17d444df6b75f9c0ecb6dc570a940abeab22ba3b Mon Sep 17 00:00:00 2001 From: Loic Guilmard Date: Wed, 18 Dec 2024 17:09:41 +0100 Subject: [PATCH 31/57] migrated to pyproject.toml --- hatch_build.py | 10 ++++++++ plugin_info.toml | 23 ----------------- pyproject.toml | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 23 deletions(-) create mode 100644 hatch_build.py delete mode 100644 plugin_info.toml create mode 100644 pyproject.toml diff --git a/hatch_build.py b/hatch_build.py new file mode 100644 index 0000000..a0208ee --- /dev/null +++ b/hatch_build.py @@ -0,0 +1,10 @@ +from pathlib import Path +from hatchling.metadata.plugin.interface import MetadataHookInterface +from pymodaq_utils.resources.hatch_build_plugins import update_metadata_from_toml + +here = Path(__file__).absolute().parent + + +class PluginInfoTomlHook(MetadataHookInterface): + def update(self, metadata: dict) -> None: + update_metadata_from_toml(metadata, here) diff --git a/plugin_info.toml b/plugin_info.toml deleted file mode 100644 index 946fe9e..0000000 --- a/plugin_info.toml +++ /dev/null @@ -1,23 +0,0 @@ -## To modify by developper(s) of the plugin - -[plugin-info] -SHORT_PLUGIN_NAME = 'daqmx' #for instance daqmx -package-url = 'https://github.com/PyMoDAQ/pymodaq_plugins_daqmx' #to modify -description = 'Hardware plugins for PyMoDAQ using the NiDAQmx framework (pydaqmx wrapper)' - -author = 'Sébastien Weber' -author-email = 'sebastien.weber@cemes.fr' -license = 'MIT' - -[plugin-install] -#packages required for your plugin: -packages-required = ['pydaqmx','nidaqmx','pymodaq>4.0.1'] -## - -[features] # defines the plugin features contained into this plugin -instruments = true # true if plugin contains instrument classes (else false, notice the lowercase for toml files) -extensions = false # true if plugins contains dashboard extensions -pid_models = false # true if plugins contains pid models -h5exporters = false # true if plugin contains custom h5 file exporters -scanners = false # true if plugin contains custom scan layout (daq_scan extensions) - diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..329457c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,64 @@ +[features] # defines the plugin features contained into this plugin +instruments = true # true if plugin contains instrument classes (else false, notice the lowercase for toml files) +extensions = false # true if plugins contains dashboard extensions +models = false # true if plugins contains pid models +h5exporters = false # true if plugin contains custom h5 file exporters +scanners = false # true if plugin contains custom scan layout (daq_scan extensions) + +[urls] +package-url = 'https://github.com/PyMoDAQ/pymodaq_plugins_daqmx' + +[project] +name = "pymodaq_plugins_daqmx" +description = 'Hardware plugins for PyMoDAQ using the NiDAQmx framework (nidaq wrapper)' +dependencies = [ + "pymodaq>=4.4", + "pydaqmx", + "nidaqmx" +] + +authors = [ + {name = 'Sébastien Weber', email = 'sebastien.weber@cemes.fr'}, + {name = 'Sébastien Guerrero', email = 'sebastien.guerrero@insa-lyon.fr'}, +] +maintainers = [ + {name = 'Sébastien Weber', email = 'sebastien.weber@cemes.fr'}, + {name = 'Sébastien Guerrero', email = 'sebastien.guerrero@insa-lyon.fr'}, +] + +# nottodo: leave everything below as is! + +dynamic = ["version", "urls", "entry-points"] +readme = "README.rst" +license = { file="LICENSE" } +requires-python = ">=3.8" + +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Topic :: Scientific/Engineering :: Human Machine Interfaces", + "Topic :: Scientific/Engineering :: Visualization", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Software Development :: User Interfaces", +] + +[build-system] +requires = [ + "hatchling>=1.9.0", + "hatch-vcs", "toml", + "pymodaq_utils>=0.0.6", +] +build-backend = "hatchling.build" + +[tool.hatch.metadata.hooks.custom] + +[tool.hatch.version] +source = "vcs" + From ba2ec80cf5b2697f9ff28d7356f173ff0ba48ead Mon Sep 17 00:00:00 2001 From: Loic Guilmard Date: Thu, 19 Dec 2024 10:44:06 +0100 Subject: [PATCH 32/57] extended nidaqmx.constants : UsageTypeAI, Edge, TerminalConfiguration, ThermocoupleType --- .../hardware/national_instruments/__init__.py | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/__init__.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/__init__.py index e69de29..987567a 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/__init__.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/__init__.py @@ -0,0 +1,47 @@ +from aenum import extend_enum +from types import MethodType +from nidaqmx.constants import UsageTypeAI, Edge, TerminalConfiguration, ThermocoupleType + + +def members(classref): + """ + Return the list of the members of cls.__members__.items() + """ + return list(classref._member_map_.values()) + + +def names(classref): + """ + Return the list of the names of the enum members + """ + return classref._member_names_ + + +extend_enum(UsageTypeAI, "Thermocouple", UsageTypeAI.TEMPERATURE_THERMOCOUPLE.value) + + +extend_enum(TerminalConfiguration, "Auto", TerminalConfiguration.DEFAULT.value) + + +UsageTypeAI.members = MethodType(members, UsageTypeAI) + + +UsageTypeAI.names = MethodType(names, UsageTypeAI) + + +Edge.members = MethodType(members, Edge) + + +Edge.names = MethodType(names, Edge) + + +TerminalConfiguration.members = MethodType(members, TerminalConfiguration) + + +TerminalConfiguration.names = MethodType(names, TerminalConfiguration) + + +ThermocoupleType.members = MethodType(members, ThermocoupleType) + + +ThermocoupleType.names = MethodType(names, ThermocoupleType) From d91685624ac9407b77e06c0a466b4ae4bc5ff9db Mon Sep 17 00:00:00 2001 From: Loic Guilmard Date: Thu, 19 Dec 2024 10:47:46 +0100 Subject: [PATCH 33/57] Using the extended nidaqmx.constants --- .../national_instruments/daq_NIDAQmx.py | 15 ++++++----- .../hardware/national_instruments/daqmxni.py | 25 ++++++------------- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index eebfee0..b9caa0b 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -7,10 +7,9 @@ import traceback from pymodaq.utils.parameter import Parameter from pymodaq.utils.parameter.pymodaq_ptypes import registerParameterType, GroupParameter -from .daqmxni import DAQmx, Edge, DAQ_NIDAQ_source, \ - ClockSettings, AIChannel, Counter, AIThermoChannel, AOChannel, TriggerSettings, DOChannel, DIChannel -logger = set_logger(get_module_name(__file__)) -from .daqmxni import UsageTypeAI, ThermocoupleType, TerminalConfiguration +from .daqmxni import DAQmx, Edge, DAQ_NIDAQ_source, ClockSettings, AIChannel, Counter, \ + AIThermoChannel, AOChannel, TriggerSettings, DOChannel, DIChannel +from . import UsageTypeAI, ThermocoupleType, TerminalConfiguration from pymodaq.utils.daq_utils import ThreadCommand, getLineInfo from pymodaq.utils.data import DataToExport, DataFromPlugins, DataActuator from easydict import EasyDict as edict @@ -31,7 +30,7 @@ class ScalableGroupAI(GroupParameter): hardware.DAQ_Move_Stage_type """ - params = [{'title': 'AI type:', 'name': 'ai_type', 'type': 'list', 'limits': UsageTypeAI._member_names_}, + params = [{'title': 'AI type:', 'name': 'ai_type', 'type': 'list', 'limits': UsageTypeAI.names()}, {'title': 'Voltages:', 'name': 'voltage_settings', 'type': 'group', 'children': [ {'title': 'Voltage Min:', 'name': 'volt_min', 'type': 'float', 'value': -10.}, {'title': 'Voltage Max:', 'name': 'volt_max', 'type': 'float', 'value': 10.}, @@ -41,12 +40,12 @@ class ScalableGroupAI(GroupParameter): {'title': 'Current Max:', 'name': 'curr_max', 'type': 'float', 'value': 1, 'suffix': 'A'}, ]}, {'title': 'Thermocouple:', 'name': 'thermoc_settings', 'type': 'group', 'visible': False, 'children': [ - {'title': 'Thc. type:', 'name': 'thermoc_type', 'type': 'list', 'limits': ThermocoupleType._member_names_, + {'title': 'Thc. type:', 'name': 'thermoc_type', 'type': 'list', 'limits': ThermocoupleType.names(), 'value': 'K'}, {'title': 'Temp. Min (°C):', 'name': 'T_min', 'type': 'float', 'value': 0, 'suffix': '°C'}, {'title': 'Temp. Max (°C):', 'name': 'T_max', 'type': 'float', 'value': 50, 'suffix': '°C'}, ]}, - {'title': 'Termination:', 'name': 'termination', 'type': 'list', 'limits': TerminalConfiguration._member_names_}, + {'title': 'Termination:', 'name': 'termination', 'type': 'list', 'limits': TerminalConfiguration.names()}, ] def __init__(self, **opts): @@ -93,7 +92,7 @@ class ScalableGroupAO(GroupParameter): hardware.DAQ_Move_Stage_type """ - params = [{'title': 'AO type:', 'name': 'ao_type', 'type': 'list', 'limits': UsageTypeAI._member_names_[0:2]}, + params = [{'title': 'AO type:', 'name': 'ao_type', 'type': 'list', 'limits': UsageTypeAI.names()[0:2]}, {'title': 'Voltages:', 'name': 'voltage_settings', 'type': 'group', 'children': [ {'title': 'Voltage Min:', 'name': 'volt_min', 'type': 'list', 'value': -10., }, {'title': 'Voltage Max:', 'name': 'volt_max', 'type': 'list', 'value': 10., }, diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index 19aac63..5a4e2f3 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -4,11 +4,13 @@ import numpy as np from pymodaq.utils.logger import set_logger, get_module_name -from nidaqmx.constants import UsageTypeAI, ThermocoupleType, TerminalConfiguration, Edge, AcquisitionType, \ - VoltageUnits, CurrentUnits, CurrentShuntResistorLocation, TemperatureUnits, \ - CJCSource, CountDirection, Level, FrequencyUnits, TimeUnits, LineGrouping from nidaqmx.system import System +from nidaqmx.constants import AcquisitionType, VoltageUnits, CurrentUnits, CurrentShuntResistorLocation, \ + TemperatureUnits, CJCSource, CountDirection, Level, FrequencyUnits, TimeUnits, \ + LineGrouping +from . import UsageTypeAI, Edge, TerminalConfiguration, ThermocoupleType + from nidaqmx.system.device import Device from nidaqmx import Task from nidaqmx.errors import DaqError, DAQmxErrors @@ -58,17 +60,6 @@ class DAQ_NIDAQ_source(IntEnumExtend): Terminals = 5 -# class DAQ_analog_types(UsageTypeAI): -# """ -# Enum class of Ai types -# -# =============== ========== -# **Attributes** **Type** -# =============== ========== -# """ -# Thermocouple = UsageTypeAI.TEMPERATURE_THERMOCOUPLE.value - - class ClockSettingsBase: def __init__(self, Nsamples=1000, repetition=False): @@ -131,14 +122,14 @@ def __init__(self, analog_type=UsageTypeAI.VOLTAGE, value_min=-10., value_max=+1 class AIChannel(AChannel): def __init__(self, termination=TerminalConfiguration.DEFAULT, **kwargs): super().__init__(**kwargs) - assert termination in TerminalConfiguration._member_map_.values() + assert termination in TerminalConfiguration.members() self.termination = termination class AIThermoChannel(AIChannel): def __init__(self, thermo_type=ThermocoupleType.K, **kwargs): super().__init__(**kwargs) - assert thermo_type in ThermocoupleType._member_map_.values() + assert thermo_type in ThermocoupleType.members() self.thermo_type = thermo_type @@ -149,7 +140,7 @@ def __init__(self, **kwargs): class Counter(Channel): def __init__(self, edge=Edge.RISING, **kwargs): - assert edge in Edge._member_map_.values() + assert edge in Edge.members() super().__init__(**kwargs) self.edge = edge self.counter_type = "Edge Counter" From 0e4be2eca11dc5bb386f06132227ec962662bd5a Mon Sep 17 00:00:00 2001 From: Loic Guilmard Date: Thu, 19 Dec 2024 10:51:20 +0100 Subject: [PATCH 34/57] [Cleaning] aliasing task and System, using explicit enum.Input and PEPing --- .../plugins_0D/daq_0Dviewer_NIDAQmx.py | 10 +++--- .../national_instruments/daq_NIDAQmx.py | 34 ++++++++++++------- .../hardware/national_instruments/daqmxni.py | 23 ++++++------- 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py index 10dbd42..b5cdb49 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py @@ -2,7 +2,7 @@ from pymodaq.control_modules.viewer_utility_classes import comon_parameters as viewer_params from pymodaq_plugins_daqmx import config from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import AIChannel, AIThermoChannel, DAQmx, \ - Task, niconstants + niTask, niconstants from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import UsageTypeAI, ThermocoupleType, \ TerminalConfiguration from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_base @@ -64,10 +64,10 @@ def close(self): else: try: print("In main") - import nidaqmx.system.System + from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import niSystem # EXPLORE DEVICES - devices = nidaqmx.system.System.local().devices + devices = niSystem.local().devices print("devices {}".format(devices)) print("devices names {}".format(devices.device_names)) print("devices types {}".format([dev.product_type for dev in devices])) @@ -112,8 +112,8 @@ def close(self): ), ] # CREATE TASK - task_9211 = Task() - task_9205 = Task() + task_9211 = niTask() + task_9205 = niTask() def callback_9211(task_handle, every_n_samples_event_type, number_of_samples, callback_data): data9211 = task_9211.read(5) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index b9caa0b..095ef82 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -12,7 +12,10 @@ from . import UsageTypeAI, ThermocoupleType, TerminalConfiguration from pymodaq.utils.daq_utils import ThreadCommand, getLineInfo from pymodaq.utils.data import DataToExport, DataFromPlugins, DataActuator -from easydict import EasyDict as edict +from easydict import EasyDict as eDict + + +logger = set_logger(get_module_name(__file__)) class ScalableGroupAI(GroupParameter): @@ -329,7 +332,7 @@ def commit_settings(self, param: Parameter): if param.name() == 'NIDAQ_type': self.controller.update_NIDAQ_channels(param.value()) - if param.value() == DAQ_NIDAQ_source(0).name: # analog input + if param.value() == DAQ_NIDAQ_source.Analog_Input.name: # analog input self.settings.child('clock_settings').show() self.settings.child('ai_channels').show() self.settings.child('ao_channels').hide() @@ -338,7 +341,7 @@ def commit_settings(self, param: Parameter): self.settings.child('do_channels').hide() self.settings.child('di_channels').hide() - elif param.value() == DAQ_NIDAQ_source(1).name: # analog output + elif param.value() == DAQ_NIDAQ_source.Analog_Output.name: # analog output self.settings.child('clock_settings').show() self.settings.child('ai_channels').hide() self.settings.child('ao_channels').show() @@ -347,7 +350,7 @@ def commit_settings(self, param: Parameter): self.settings.child('do_channels').hide() self.settings.child('di_channels').hide() - elif param.value() == DAQ_NIDAQ_source(2).name: # counter input + elif param.value() == DAQ_NIDAQ_source.Counter.name: # counter input self.settings.child('clock_settings').hide() self.settings.child('ai_channels').hide() self.settings.child('ao_channels').hide() @@ -356,7 +359,7 @@ def commit_settings(self, param: Parameter): self.settings.child('do_channels').hide() self.settings.child('di_channels').hide() - elif param.value() == DAQ_NIDAQ_source(3).name: # Digital_Input + elif param.value() == DAQ_NIDAQ_source.Digital_Input.name: # Digital_Input self.settings.child('clock_settings').show() self.settings.child('ai_channels').hide() self.settings.child('ao_channels').show() @@ -365,7 +368,8 @@ def commit_settings(self, param: Parameter): self.settings.child('do_channels').hide() self.settings.child('di_channels').show() - elif param.value() == DAQ_NIDAQ_source(4).name: # digital output + + elif param.value() == DAQ_NIDAQ_source.Digital_Output.name: # digital output self.settings.child('clock_settings').show() self.settings.child('ai_channels').hide() self.settings.child('ao_channels').hide() @@ -373,6 +377,7 @@ def commit_settings(self, param: Parameter): self.settings.child('counter_settings').hide() self.settings.child('do_channels').show() self.settings.child('di_channels').hide() + self.update_task() elif param.name() == 'refresh_hardware': @@ -415,7 +420,7 @@ def update_task(self): def get_channels_from_settings(self): channels = [] - if self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(0).name: # analog input + if self.settings['NIDAQ_type'] == DAQ_NIDAQ_source.Analog_Input.name: # analog input for channel in self.settings.child('ai_channels').children(): logger.info("get_channels_from_settings - channel {}".format(channel)) analog_type = channel['ai_type'] @@ -440,7 +445,7 @@ def get_channels_from_settings(self): thermo_type=ThermocoupleType[ channel['thermoc_settings', 'thermoc_type']], )) - elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(1).name: # analog output + elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source.Analog_Output.name: # analog output for channel in self.settings.child('ao_channels').children(): analog_type = channel['ao_type'] channels.append(AOChannel(name=channel.opts['title'], @@ -449,20 +454,22 @@ def get_channels_from_settings(self): value_max=channel['voltage_settings', 'volt_max'], )) - elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(2).name: # counter input + elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source.Counter.name: # counter input for channel in self.settings.child('counter_settings', 'counter_channels').children(): channels.append(Counter(name=channel.opts['title'], source='Counter', edge=channel['edge'])) - elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(3).name: # digital input + + elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source.Digital_Input.name: # digital input for channel in self.settings.child('di_channels').children(): channels.append(DIChannel(name=channel.opts['title'], source='Digital_Input')) - elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(4).name: # Digital output + elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source.Digital_Output.name: # Digital output for channel in self.settings.child('do_channels').children(): channels.append(DOChannel(name=channel.opts['title'], source='Digital_Output')) + channels = [ch for ch in channels if self.settings.child("dev_to_use").value() in ch.name] return channels @@ -635,6 +642,7 @@ def counter_done(self): # y_axis=Axis(label='Count Number', units='1/s'))]) self.task.StopTask() + class DAQ_NIDAQmx_Actuator(DAQ_Move_base, DAQ_NIDAQmx_base): """ Wrapper object to access the Mock fonctionnalities, similar wrapper for all controllers. @@ -717,7 +725,7 @@ def ini_stage(self, controller=None): Returns ------- - self.status (edict): with initialization status: three fields: + self.status (easydict): with initialization status: three fields: * info (str) * controller (object) initialized controller *initialized: (bool): False if initialization failed otherwise True @@ -728,7 +736,7 @@ def ini_stage(self, controller=None): # controller is an object that may be passed to other instances of DAQ_Move_Mock in case # of one controller controlling multiactuators (or detector) - self.status.update(edict(info="", controller=None, initialized=False)) + self.status.update(eDict(info="", controller=None, initialized=False)) # check whether this stage is controlled by a multiaxe controller (to be defined for each plugin) # if multiaxes then init the controller here if Master state otherwise use external controller diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index 5a4e2f3..606bdea 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -4,15 +4,14 @@ import numpy as np from pymodaq.utils.logger import set_logger, get_module_name -from nidaqmx.system import System - from nidaqmx.constants import AcquisitionType, VoltageUnits, CurrentUnits, CurrentShuntResistorLocation, \ TemperatureUnits, CJCSource, CountDirection, Level, FrequencyUnits, TimeUnits, \ LineGrouping from . import UsageTypeAI, Edge, TerminalConfiguration, ThermocoupleType +from nidaqmx.system import System as niSystem from nidaqmx.system.device import Device -from nidaqmx import Task +from nidaqmx import Task as niTask from nidaqmx.errors import DaqError, DAQmxErrors from pymodaq_plugins_daqmx import config @@ -209,7 +208,7 @@ def get_NIDAQ_devices(cls): list of devices as strings to be used in subsequent commands """ try: - devices = System.local().devices + devices = niSystem.local().devices if devices == ['']: devices = [] return devices @@ -286,7 +285,7 @@ def configuration_sequence(self, viewer, current_device): if not device_name == current_device.name: continue device_product = config["NIDAQ_Devices", dev].get('product') - device = nidaqmx.system.device.Device(device_name) + device = Device(device_name) assert device in self.devices and device.product_type == device_product, device.name except AssertionError as err: logger.error("Device {} not detected: {}".format(device_name, err)) @@ -297,7 +296,7 @@ def configuration_sequence(self, viewer, current_device): try: module_name = config["NIDAQ_Devices", dev, mod].get('name') module_product = config["NIDAQ_Devices", dev, mod].get('product') - module = nidaqmx.system.device.Device(module_name) + module = Device(module_name) assert module in self.devices and module.product_type == module_product, module.name viewer.config_modules.append(config["NIDAQ_Devices", dev, mod].get('name')) except AssertionError as err: @@ -318,7 +317,7 @@ def configuration_sequence(self, viewer, current_device): analog_type=ai[ch].get("analog_type"), value_min=float(ai[ch].get("value_min")), value_max=float(ai[ch].get("value_max")), - termination=DAQ_termination.__getitem__(term), + termination=TerminalConfiguration.__getitem__(term), )) elif ai[ch].get("analog_type") == "Current": viewer.config_channels.append(AIChannel @@ -327,7 +326,7 @@ def configuration_sequence(self, viewer, current_device): analog_type=ai[ch].get("analog_type"), value_min=float(ai[ch].get("value_min")), value_max=float(ai[ch].get("value_max")), - termination=DAQ_termination.__getitem__(term), + termination=TerminalConfiguration.__getitem__(term), )) elif ai[ch].get("analog_type") == "Thermocouple": th = ai[ch].get("thermo_type") @@ -388,13 +387,13 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti try: if self._task is not None: - if isinstance(self._task, Task): + if isinstance(self._task, niTask): self._task.close() self._task = None self.c_callback = None - self._task = Task() + self._task = niTask() logger.info("TASK: {}".format(self._task)) err_code = None @@ -579,12 +578,12 @@ def readCounter(self): @classmethod def getAIVoltageRange(cls, device='Dev1'): - ret = System.local().devices[device].ai_voltage_rngs # todo self.devices[device].ai_voltage_rngs + ret = niSystem.local().devices[device].ai_voltage_rngs # todo self.devices[device].ai_voltage_rngs return [tuple(ret[6:8])] @classmethod def getAOVoltageRange(cls, device='Dev1'): - ret = System.local().devices[device].ao_voltage_rngs # todo self.devices[device].ao_voltage_rngs + ret = niSystem.local().devices[device].ao_voltage_rngs # todo self.devices[device].ao_voltage_rngs return [tuple(ret)] # [(-10., 10.)] Why this format is needed?? def stop(self): From 974fbd17a57dfdca2149fe9096a6caada9a501bc Mon Sep 17 00:00:00 2001 From: Loic Guilmard Date: Thu, 19 Dec 2024 12:38:41 +0100 Subject: [PATCH 35/57] [Cleaning] aliasing Device and typos --- .../national_instruments/daq_NIDAQmx.py | 4 +-- .../hardware/national_instruments/daqmxni.py | 32 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index 095ef82..83eb936 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -878,5 +878,5 @@ def stop_motion(self): self.stop() self.emit_status(ThreadCommand('Update_Status', ['Some info you want to log'])) self.move_done() # to let the interface know the actuator stopped - ############################## ->>>>>>> eda1396 (removed CustomEnum to use the ones from nidaq.constants) + + diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index 606bdea..5d4de57 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -10,7 +10,7 @@ from . import UsageTypeAI, Edge, TerminalConfiguration, ThermocoupleType from nidaqmx.system import System as niSystem -from nidaqmx.system.device import Device +from nidaqmx.system.device import Device as niDevice from nidaqmx import Task as niTask from nidaqmx.errors import DaqError, DAQmxErrors from pymodaq_plugins_daqmx import config @@ -247,17 +247,17 @@ def get_NIDAQ_channels(cls, devices=None, source_type=None): for device in devices: for source in source_type: if source == DAQ_NIDAQ_source.Analog_Input.name: # analog input - channels = Device(device).ai_physical_chans.channel_names + channels = niDevice(device).ai_physical_chans.channel_names elif source == DAQ_NIDAQ_source.Analog_Output.name: # analog output - channels = Device(device).ao_physical_chans.channel_names + channels = niDevice(device).ao_physical_chans.channel_names elif source == DAQ_NIDAQ_source.Counter.name: # counter - channels = Device(device).ci_physical_chans.channel_names + channels = niDevice(device).ci_physical_chans.channel_names elif source == DAQ_NIDAQ_source.Digital_Output.name: # digital output - channels = Device(device).do_lines.channel_names + channels = niDevice(device).do_lines.channel_names elif source == DAQ_NIDAQ_source.Digital_Input.name: # digital iutput - channels = Device(device).di_lines.channel_names + channels = niDevice(device).di_lines.channel_names elif source == DAQ_NIDAQ_source.Terminals.name: # terminals - channels = Device(device).terminals + channels = niDevice(device).terminals if channels != ['']: channels_tot.extend(channels) @@ -285,7 +285,7 @@ def configuration_sequence(self, viewer, current_device): if not device_name == current_device.name: continue device_product = config["NIDAQ_Devices", dev].get('product') - device = Device(device_name) + device = niDevice(device_name) assert device in self.devices and device.product_type == device_product, device.name except AssertionError as err: logger.error("Device {} not detected: {}".format(device_name, err)) @@ -296,7 +296,7 @@ def configuration_sequence(self, viewer, current_device): try: module_name = config["NIDAQ_Devices", dev, mod].get('name') module_product = config["NIDAQ_Devices", dev, mod].get('product') - module = Device(module_name) + module = niDevice(module_name) assert module in self.devices and module.product_type == module_product, module.name viewer.config_modules.append(config["NIDAQ_Devices", dev, mod].get('name')) except AssertionError as err: @@ -336,7 +336,7 @@ def configuration_sequence(self, viewer, current_device): analog_type=ai[ch].get("analog_type"), value_min=float(ai[ch].get("value_min")), value_max=float(ai[ch].get("value_max")), - thermo_type=DAQ_thermocouples.__getitem__(th), + thermo_type=ThermocoupleType.__getitem__(th), )) logger.info("Devices from config: {}".format(viewer.config_devices)) logger.info("Current device: {}".format(current_device)) @@ -351,19 +351,19 @@ def configuration_sequence(self, viewer, current_device): @classmethod def getAOMaxRate(cls, device): - return Device(device).ao_max_rate + return niDevice(device).ao_max_rate @classmethod def getAIMaxRate(cls, device): - return Device(device).ai_max_single_chan_rate + return niDevice(device).ai_max_single_chan_rate @classmethod def isAnalogTriggeringSupported(cls, device): - return Device(device).anlg_trig_supported + return niDevice(device).anlg_trig_supported @classmethod def isDigitalTriggeringSupported(cls, device): - return Device(device).dig_trig_supported + return niDevice(device).dig_trig_supported @classmethod def getTriggeringSources(cls, devices=None): @@ -373,12 +373,12 @@ def getTriggeringSources(cls, devices=None): for device in devices: if cls.isDigitalTriggeringSupported(device): - string = Device(device).terminals + string = niDevice(device).terminals channels = [chan for chan in string if 'PFI' in chan] if channels != ['']: sources.extend(channels) if cls.isAnalogTriggeringSupported(device): - channels = Device(device).ai_physical_chans.channel_names + channels = niDevice(device).ai_physical_chans.channel_names if channels != ['']: sources.extend(channels) return sources From a964c7da279e4ee1a9ee8333e94fe8a7ad0def58 Mon Sep 17 00:00:00 2001 From: Loic Guilmard Date: Thu, 19 Dec 2024 14:26:18 +0100 Subject: [PATCH 36/57] Fixes --- .../plugins_0D/daq_0Dviewer_NIDAQmx.py | 24 +++++++++---------- .../national_instruments/daq_NIDAQmx.py | 23 +++++++++--------- .../daq_NIDAQmx_Viewer.py | 4 ++-- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py index b5cdb49..94fd711 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py @@ -1,10 +1,10 @@ from pymodaq.control_modules.viewer_utility_classes import main from pymodaq.control_modules.viewer_utility_classes import comon_parameters as viewer_params from pymodaq_plugins_daqmx import config -from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import AIChannel, AIThermoChannel, DAQmx, \ - niTask, niconstants +from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import AIChannel, AIThermoChannel, DAQmx, niTask, \ + niDevice, TemperatureUnits, CJCSource, VoltageUnits, AcquisitionType from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import UsageTypeAI, ThermocoupleType, \ - TerminalConfiguration + TerminalConfiguration, Edge from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_base from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx_Viewer import DAQ_NIDAQmx_Viewer from pymodaq.utils.logger import set_logger, get_module_name @@ -18,11 +18,11 @@ class DAQ_0DViewer_NIDAQmx(DAQ_NIDAQmx_Viewer): config_channels: list channels_ai: list - config: config + config: config # todo review Useful/Unused controller: DAQmx config_devices: list config_modules: list - current_device: nidaqmx.system.Device + current_device: niDevice live: bool Naverage: int @@ -128,9 +128,9 @@ def callback_9205(task_handle, every_n_samples_event_type, number_of_samples, ca "", channel.value_min, channel.value_max, - niconstants.TemperatureUnits.DEG_C, + TemperatureUnits.DEG_C, channel.thermo_type, - niconstants.CJCSource.BUILT_IN, + CJCSource.BUILT_IN, 0., "") for channel in channels_voltage: @@ -139,14 +139,14 @@ def callback_9205(task_handle, every_n_samples_event_type, number_of_samples, ca channel.termination, channel.value_min, channel.value_max, - niconstants.VoltageUnits.VOLTS, + VoltageUnits.VOLTS, "") - task_9211.timing.cfg_samp_clk_timing(5.0, None, niconstants.Edge.RISING, - niconstants.AcquisitionType.CONTINUOUS, 5) + task_9211.timing.cfg_samp_clk_timing(5.0, None, Edge.RISING, + AcquisitionType.CONTINUOUS, 5) task_9211.register_every_n_samples_acquired_into_buffer_event(10, callback_9211) - task_9205.timing.cfg_samp_clk_timing(10, None, niconstants.Edge.RISING, - niconstants.AcquisitionType.CONTINUOUS, 10) + task_9205.timing.cfg_samp_clk_timing(10, None, Edge.RISING, + AcquisitionType.CONTINUOUS, 10) task_9205.register_every_n_samples_acquired_into_buffer_event(2, callback_9205) task_9211.start() diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index 83eb936..073f2f9 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -7,9 +7,9 @@ import traceback from pymodaq.utils.parameter import Parameter from pymodaq.utils.parameter.pymodaq_ptypes import registerParameterType, GroupParameter -from .daqmxni import DAQmx, Edge, DAQ_NIDAQ_source, ClockSettings, AIChannel, Counter, \ - AIThermoChannel, AOChannel, TriggerSettings, DOChannel, DIChannel -from . import UsageTypeAI, ThermocoupleType, TerminalConfiguration +from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import DAQmx, Edge, DAQ_NIDAQ_source, ClockSettings, \ + AIChannel, Counter, AIThermoChannel, AOChannel, TriggerSettings, DOChannel, DIChannel, UsageTypeAI, \ + ThermocoupleType, TerminalConfiguration from pymodaq.utils.daq_utils import ThreadCommand, getLineInfo from pymodaq.utils.data import DataToExport, DataFromPlugins, DataActuator from easydict import EasyDict as eDict @@ -413,7 +413,7 @@ def update_task(self): self.trigger_settings = \ TriggerSettings(trig_source=self.settings['trigger_settings', 'trigger_channel'], enable=self.settings['trigger_settings', 'enable'], - edge=Edge[self.settings['trigger_settings', 'edge']], + edge=Edge[self.settings['trigger_settings', 'edge'].upper()], level=self.settings['trigger_settings', 'level'], ) if not not self.channels: self.controller.update_task(self.channels, self.clock_settings, trigger_settings=self.trigger_settings) @@ -429,19 +429,19 @@ def get_channels_from_settings(self): source='Analog_Input', analog_type=analog_type, value_min=channel['voltage_settings', 'volt_min'], value_max=channel['voltage_settings', 'volt_max'], - termination=TerminalConfiguration[channel['termination']], )) + termination=TerminalConfiguration[channel['termination'].upper()], )) elif analog_type == 'Current': channels.append(AIChannel(name=channel.opts['title'], source='Analog_Input', analog_type=analog_type, value_min=channel['current_settings', 'curr_min'], value_max=channel['current_settings', 'curr_max'], - termination=TerminalConfiguration[channel['termination']], )) + termination=TerminalConfiguration[channel['termination'].upper()], )) elif analog_type == 'Thermocouple': channels.append(AIThermoChannel(name=channel.opts['title'], source='Analog_Input', analog_type=analog_type, value_min=channel['thermoc_settings', 'T_min'], value_max=channel['thermoc_settings', 'T_max'], - termination=channel['termination'], + termination=TerminalConfiguration[channel['termination'].upper()], thermo_type=ThermocoupleType[ channel['thermoc_settings', 'thermoc_type']], )) @@ -459,7 +459,6 @@ def get_channels_from_settings(self): channels.append(Counter(name=channel.opts['title'], source='Counter', edge=channel['edge'])) - elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source.Digital_Input.name: # digital input for channel in self.settings.child('di_channels').children(): channels.append(DIChannel(name=channel.opts['title'], @@ -666,7 +665,7 @@ class DAQ_NIDAQmx_Actuator(DAQ_Move_base, DAQ_NIDAQmx_base): {'title': 'Status:', 'name': 'multi_status', 'type': 'list', 'value': 'Master', 'limits': ['Master', 'Slave']}, {'title': 'Axis:', 'name': 'axis', 'type': 'list', 'limits': stage_names}, - ]}] + actuator_params + ]}] + actuator_params() def __init__(self, parent=None, params_state=None, control_type="Actuator"): DAQ_Move_base.__init__(self, parent, params_state) # defines settings attribute and various other methods @@ -783,7 +782,7 @@ def calulate_waveform(self, value): return values - def move_Abs(self, position): + def move_abs(self, position): """ Move the actuator to the absolute target defined by position Parameters @@ -820,7 +819,7 @@ def move_done_callback(self, taskhandle, status, callbackdata): self.task.StopTask() return 0 - def move_Rel(self, position): + def move_rel(self, position): """ Move the actuator to the relative target actuator value defined by position Parameters @@ -850,7 +849,7 @@ def move_Rel(self, position): elif self.settings['NIDAQ_type'] == 'Digital_Output': self.writeDigital(1, np.array([self.target_position], dtype=np.uint8), autostart=True) - def move_Home(self): + def move_home(self): """ Send the update status thread command. See Also diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py index 0069ae2..248da5c 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py @@ -3,7 +3,7 @@ import traceback from qtpy import QtCore from .daqmxni import DAQmx -from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_base, DAQ_termination +from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_base, TerminalConfiguration from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters as viewer_params from pymodaq.utils.daq_utils import ThreadCommand from pymodaq.utils.data import DataFromPlugins, DataToExport @@ -139,7 +139,7 @@ def ini_detector(self, controller=None): "thermoc_settings", "T_max").setValue(ch.value_max) self.settings.child("ai_channels", param.opts['name'], "termination").setValue( - DAQ_termination.Auto) + TerminalConfiguration.DEFAULT) info = "Plugin Initialized" initialized = True logger.info("Detector 0D initialized") From d4731a9cb44e5dc10b424394325809f520aeb145 Mon Sep 17 00:00:00 2001 From: Loic Guilmard Date: Thu, 19 Dec 2024 15:58:33 +0100 Subject: [PATCH 37/57] using enums instead of hardcoded strings. --- .../national_instruments/daq_NIDAQmx.py | 9 ++--- .../daq_NIDAQmx_Viewer.py | 15 ++++---- .../hardware/national_instruments/daqmxni.py | 38 +++++++++---------- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index 073f2f9..a772ede 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -34,7 +34,7 @@ class ScalableGroupAI(GroupParameter): """ params = [{'title': 'AI type:', 'name': 'ai_type', 'type': 'list', 'limits': UsageTypeAI.names()}, - {'title': 'Voltages:', 'name': 'voltage_settings', 'type': 'group', 'children': [ + {'title': 'Voltage:', 'name': 'voltage_settings', 'type': 'group', 'children': [ {'title': 'Voltage Min:', 'name': 'volt_min', 'type': 'float', 'value': -10.}, {'title': 'Voltage Max:', 'name': 'volt_max', 'type': 'float', 'value': 10.}, ]}, @@ -424,24 +424,23 @@ def get_channels_from_settings(self): for channel in self.settings.child('ai_channels').children(): logger.info("get_channels_from_settings - channel {}".format(channel)) analog_type = channel['ai_type'] - if analog_type == 'Voltage': + if analog_type == UsageTypeAI.VOLTAGE.name: channels.append(AIChannel(name=channel.opts['title'], source='Analog_Input', analog_type=analog_type, value_min=channel['voltage_settings', 'volt_min'], value_max=channel['voltage_settings', 'volt_max'], termination=TerminalConfiguration[channel['termination'].upper()], )) - elif analog_type == 'Current': + elif analog_type == UsageTypeAI.CURRENT.name: channels.append(AIChannel(name=channel.opts['title'], source='Analog_Input', analog_type=analog_type, value_min=channel['current_settings', 'curr_min'], value_max=channel['current_settings', 'curr_max'], termination=TerminalConfiguration[channel['termination'].upper()], )) - elif analog_type == 'Thermocouple': + elif analog_type == UsageTypeAI.TEMPERATURE_THERMOCOUPLE.name: channels.append(AIThermoChannel(name=channel.opts['title'], source='Analog_Input', analog_type=analog_type, value_min=channel['thermoc_settings', 'T_min'], value_max=channel['thermoc_settings', 'T_max'], - termination=TerminalConfiguration[channel['termination'].upper()], thermo_type=ThermocoupleType[ channel['thermoc_settings', 'thermoc_type']], )) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py index 248da5c..03f3efa 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py @@ -3,7 +3,8 @@ import traceback from qtpy import QtCore from .daqmxni import DAQmx -from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_base, TerminalConfiguration +from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_base, TerminalConfiguration, \ + UsageTypeAI from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters as viewer_params from pymodaq.utils.daq_utils import ThreadCommand from pymodaq.utils.data import DataFromPlugins, DataToExport @@ -108,24 +109,24 @@ def ini_detector(self, controller=None): self.settings.child('ai_channels').addNew(ch.name) param = [a for a in self.settings.child('ai_channels').childs if a.opts['title'] == ch.name][0] self.settings.child("ai_channels", param.opts['name'], "ai_type").setValue(ch.analog_type) - param.child("voltage_settings").show(ch.analog_type == "Voltage") - param.child("current_settings").show(ch.analog_type == "Current") - param.child("thermoc_settings").show(ch.analog_type == "Thermocouple") - if ch.analog_type == "Voltage": + param.child("voltage_settings").show(ch.analog_type == UsageTypeAI.VOLTAGE.name) + param.child("current_settings").show(ch.analog_type == UsageTypeAI.CURRENT.name) + param.child("thermoc_settings").show(ch.analog_type == UsageTypeAI.TEMPERATURE_THERMOCOUPLE.name) + if ch.analog_type == UsageTypeAI.VOLTAGE.name: self.settings.child("ai_channels", param.opts['name'], "voltage_settings", "volt_min").setValue( ch.value_min) self.settings.child("ai_channels", param.opts['name'], "voltage_settings", "volt_max").setValue( ch.value_max) self.settings.child("ai_channels", param.opts['name'], "termination").setValue( ch.termination.name) - elif ch.analog_type == "Current": + elif ch.analog_type == UsageTypeAI.CURRENT.name: self.settings.child("ai_channels", param.opts['name'], "current_settings", "curr_min").setValue( ch.value_min) self.settings.child("ai_channels", param.opts['name'], "current_settings", "curr_max").setValue( ch.value_max) self.settings.child("ai_channels", param.opts['name'], "termination").setValue( ch.termination.name) - elif ch.analog_type == "Thermocouple": + elif ch.analog_type == UsageTypeAI.TEMPERATURE_THERMOCOUPLE.name: self.settings.child("ai_channels", param.opts['name'], "thermoc_settings", diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index 5d4de57..3e2efac 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -310,7 +310,7 @@ def configuration_sequence(self, viewer, current_device): for ch in ai.keys(): name = module_name + "/" + str(ch) term = ai[ch].get("termination") - if ai[ch].get("analog_type") == "Voltage": + if ai[ch].get("analog_type") == UsageTypeAI.VOLTAGE.name: viewer.config_channels.append(AIChannel (name=name, source=ai[ch].get("source"), @@ -319,7 +319,7 @@ def configuration_sequence(self, viewer, current_device): value_max=float(ai[ch].get("value_max")), termination=TerminalConfiguration.__getitem__(term), )) - elif ai[ch].get("analog_type") == "Current": + elif ai[ch].get("analog_type") == UsageTypeAI.CURRENT.name: viewer.config_channels.append(AIChannel (name=name, source=ai[ch].get("source"), @@ -328,16 +328,16 @@ def configuration_sequence(self, viewer, current_device): value_max=float(ai[ch].get("value_max")), termination=TerminalConfiguration.__getitem__(term), )) - elif ai[ch].get("analog_type") == "Thermocouple": + elif ai[ch].get("analog_type") == UsageTypeAI.TEMPERATURE_THERMOCOUPLE.name: th = ai[ch].get("thermo_type") viewer.config_channels.append(AIThermoChannel - (name=name, - source=ai[ch].get("source"), - analog_type=ai[ch].get("analog_type"), - value_min=float(ai[ch].get("value_min")), - value_max=float(ai[ch].get("value_max")), - thermo_type=ThermocoupleType.__getitem__(th), - )) + (name=name, + source=ai[ch].get("source"), + analog_type=ai[ch].get("analog_type"), + value_min=float(ai[ch].get("value_min")), + value_max=float(ai[ch].get("value_max")), + thermo_type=ThermocoupleType.__getitem__(th), + )) logger.info("Devices from config: {}".format(viewer.config_devices)) logger.info("Current device: {}".format(current_device)) logger.info("Current device modules from config: {}".format(viewer.config_modules)) @@ -399,9 +399,9 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti # create all channels one task for one type of channels for channel in channels: - if channel.source == 'Analog_Input': # analog input + if channel.source == DAQ_NIDAQ_source.Digital_Input.name: # analog input try: - if channel.analog_type == "Voltage": + if channel.analog_type == UsageTypeAI.VOLTAGE.name: self._task.ai_channels.add_ai_voltage_chan(channel.name, "", channel.termination, @@ -410,7 +410,7 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti VoltageUnits.VOLTS, "") - elif channel.analog_type == "Current": + elif channel.analog_type == UsageTypeAI.CURRENT.name: self._task.ai_channels.add_ai_current_chan(channel.name, "", channel.termination, @@ -421,7 +421,7 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti 0., "") - elif channel.analog_type == "Thermocouple": + elif channel.analog_type == UsageTypeAI.CURRENT.name: self._task.ai_channels.add_ai_thrmcpl_chan(channel.name, "", channel.value_min, @@ -467,15 +467,15 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti if not not err_code: status = self.DAQmxGetErrorString(err_code) raise IOError(status) - elif channel.source == 'Analog_Output': # Analog_Output + elif channel.source == DAQ_NIDAQ_source.Analog_Output.name: # Analog_Output try: - if channel.analog_type == "Voltage": + if channel.analog_type == UsageTypeAI.VOLTAGE.name: self._task.ao_channels.add_ao_voltage_chan(channel.name, "", channel.value_min, channel.value_max, VoltageUnits.VOLTS, None) - elif channel.analog_type == "Current": + elif channel.analog_type == UsageTypeAI.CURRENT.name: self._task.ao_channels.add_ao_current_chan(channel.name, "", channel.value_min, channel.value_max, @@ -485,7 +485,7 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti if not not err_code: status = self.DAQmxGetErrorString(err_code) raise IOError(status) - elif channel.source == 'Digital_Output': + elif channel.source == DAQ_NIDAQ_source.Digital_Output.name: try: self._task.do_channels.add_do_chan(channel.name, "", LineGrouping.CHAN_PER_LINE) @@ -494,7 +494,7 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti if not not err_code: status = self.DAQmxGetErrorString(err_code) raise IOError(status) - elif channel.source == 'Digital_Input': # Digital_Input + elif channel.source == DAQ_NIDAQ_source.Digital_Input.name: # Digital_Input try: self._task.di_channels.add_di_chan(channel.name, "", LineGrouping.CHAN_PER_LINE) From 9797e14b50a7eee13d92d4cf7b7d2629f29b31a2 Mon Sep 17 00:00:00 2001 From: Loic Guilmard Date: Thu, 19 Dec 2024 18:19:21 +0100 Subject: [PATCH 38/57] Added DAQ_NIDAQ_source OD, 1D, Actuator --- .../national_instruments/daq_NIDAQmx.py | 42 +++++++++---------- .../daq_NIDAQmx_Viewer.py | 16 ++++--- .../hardware/national_instruments/daqmxni.py | 20 ++++++++- 3 files changed, 48 insertions(+), 30 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index a772ede..926f713 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -282,18 +282,18 @@ class DAQ_NIDAQmx_base: ] }, {'title': 'AI Channels:', 'name': 'ai_channels', 'type': 'groupai', - 'limits': DAQmx.get_NIDAQ_channels(source_type='Analog_Input')}, + 'limits': DAQmx.get_NIDAQ_channels(source_type=DAQ_NIDAQ_source.Analog_Input.name)}, {'title': 'AO Channels:', 'name': 'ao_channels', 'type': 'groupao', - 'limits': DAQmx.get_NIDAQ_channels(source_type='Analog_Output')}, + 'limits': DAQmx.get_NIDAQ_channels(source_type=DAQ_NIDAQ_source.Analog_Output.name)}, {'title': 'DO Channels:', 'name': 'do_channels', 'type': 'groupdo', - 'limits': DAQmx.get_NIDAQ_channels(source_type='Digital_Output')}, + 'limits': DAQmx.get_NIDAQ_channels(source_type=DAQ_NIDAQ_source.Digital_Output.name)}, {'title': 'DI Channels:', 'name': 'di_channels', 'type': 'groupdi', - 'limits': DAQmx.get_NIDAQ_channels(source_type='Digital_Input')}, + 'limits': DAQmx.get_NIDAQ_channels(source_type=DAQ_NIDAQ_source.Digital_Input.name)}, {'title': 'Counter Settings:', 'name': 'counter_settings', 'type': 'group', 'visible': True, 'children': [ {'title': 'Counting time (ms):', 'name': 'counting_time', 'type': 'float', 'value': 100., 'default': 100., 'min': 0.}, {'title': 'Counting Channels:', 'name': 'counter_channels', 'type': 'groupcounter', - 'limits': DAQmx.get_NIDAQ_channels(source_type='Counter')}, + 'limits': DAQmx.get_NIDAQ_channels(source_type=DAQ_NIDAQ_source.Counter.name)}, ]}, {'title': 'Trigger Settings:', 'name': 'trigger_settings', 'type': 'group', 'visible': True, 'children': [ {'title': 'Enable?:', 'name': 'enable', 'type': 'bool', 'value': False, }, @@ -368,7 +368,6 @@ def commit_settings(self, param: Parameter): self.settings.child('do_channels').hide() self.settings.child('di_channels').show() - elif param.value() == DAQ_NIDAQ_source.Digital_Output.name: # digital output self.settings.child('clock_settings').show() self.settings.child('ai_channels').hide() @@ -387,14 +386,14 @@ def commit_settings(self, param: Parameter): self.settings.child('refresh_hardware').setValue(False) elif param.name() == 'ai_type': - param.parent().child('voltage_settings').show(param.value() == 'Voltage') - param.parent().child('current_settings').show(param.value() == 'Current') - param.parent().child('thermoc_settings').show(param.value() == 'Thermocouple') + param.parent().child('voltage_settings').show(param.value() == UsageTypeAI.Voltage.name) + param.parent().child('current_settings').show(param.value() == UsageTypeAI.Current.name) + param.parent().child('thermoc_settings').show(param.value() == UsageTypeAI.TEMPERATURE_THERMOCOUPLE.name) self.update_task() elif param.name() == 'ao_type': - param.parent().child('voltage_settings').show(param.value() == 'Voltage') - param.parent().child('current_settings').show(param.value() == 'Current') + param.parent().child('voltage_settings').show(param.value() == UsageTypeAI.Voltage.name) + param.parent().child('current_settings').show(param.value() == UsageTypeAI.Current.name) self.update_task() elif param.name() == 'trigger_channel': @@ -423,22 +422,22 @@ def get_channels_from_settings(self): if self.settings['NIDAQ_type'] == DAQ_NIDAQ_source.Analog_Input.name: # analog input for channel in self.settings.child('ai_channels').children(): logger.info("get_channels_from_settings - channel {}".format(channel)) - analog_type = channel['ai_type'] + analog_type = channel['ai_type'] # todo UsageTypeAI[channel['ai_type'].upper()].name if analog_type == UsageTypeAI.VOLTAGE.name: channels.append(AIChannel(name=channel.opts['title'], - source='Analog_Input', analog_type=analog_type, + source=DAQ_NIDAQ_source.Analog_Input.name, analog_type=analog_type, value_min=channel['voltage_settings', 'volt_min'], value_max=channel['voltage_settings', 'volt_max'], termination=TerminalConfiguration[channel['termination'].upper()], )) elif analog_type == UsageTypeAI.CURRENT.name: channels.append(AIChannel(name=channel.opts['title'], - source='Analog_Input', analog_type=analog_type, + source=DAQ_NIDAQ_source.Analog_Input.name, analog_type=analog_type, value_min=channel['current_settings', 'curr_min'], value_max=channel['current_settings', 'curr_max'], termination=TerminalConfiguration[channel['termination'].upper()], )) elif analog_type == UsageTypeAI.TEMPERATURE_THERMOCOUPLE.name: channels.append(AIThermoChannel(name=channel.opts['title'], - source='Analog_Input', analog_type=analog_type, + source=DAQ_NIDAQ_source.Analog_Input.name, analog_type=analog_type, value_min=channel['thermoc_settings', 'T_min'], value_max=channel['thermoc_settings', 'T_max'], thermo_type=ThermocoupleType[ @@ -448,7 +447,7 @@ def get_channels_from_settings(self): for channel in self.settings.child('ao_channels').children(): analog_type = channel['ao_type'] channels.append(AOChannel(name=channel.opts['title'], - source='Analog_Output', analog_type=analog_type, + source=DAQ_NIDAQ_source.Analog_Output.name, analog_type=analog_type, value_min=channel['voltage_settings', 'volt_min'], value_max=channel['voltage_settings', 'volt_max'], )) @@ -461,12 +460,12 @@ def get_channels_from_settings(self): elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source.Digital_Input.name: # digital input for channel in self.settings.child('di_channels').children(): channels.append(DIChannel(name=channel.opts['title'], - source='Digital_Input')) + source=DAQ_NIDAQ_source.Digital_Input.name)) elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source.Digital_Output.name: # Digital output for channel in self.settings.child('do_channels').children(): channels.append(DOChannel(name=channel.opts['title'], - source='Digital_Output')) + source=DAQ_NIDAQ_source.Digital_Output.name)) channels = [ch for ch in channels if self.settings.child("dev_to_use").value() in ch.name] return channels @@ -505,12 +504,11 @@ def __init__(self, parent=None, params_state=None, control_type="0D"): self.live = False self.control_type = control_type # could be "0D", "1D" or "Actuator" if self.control_type == "0D": - self.settings.child('NIDAQ_type').setLimits( - ['Analog_Input', 'Counter', 'Digital_Input']) # analog input and counter + self.settings.child('NIDAQ_type').setLimits(DAQ_NIDAQ_source.Source0D()) # analog input and counter elif self.control_type == "1D": - self.settings.child('NIDAQ_type').setLimits(['Analog_Input']) + self.settings.child('NIDAQ_type').setLimits([DAQ_NIDAQ_source.sources1D()]) elif self.control_type == "Actuator": - self.settings.child('NIDAQ_type').setLimits(['Analog_Output']) + self.settings.child('NIDAQ_type').setLimits([DAQ_NIDAQ_source.Actuator()]) self.settings.child('ao_settings').hide() self.settings.child('ao_channels').hide() diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py index 03f3efa..f660ac7 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py @@ -4,7 +4,7 @@ from qtpy import QtCore from .daqmxni import DAQmx from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_base, TerminalConfiguration, \ - UsageTypeAI + UsageTypeAI, DAQ_NIDAQ_source from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters as viewer_params from pymodaq.utils.daq_utils import ThreadCommand from pymodaq.utils.data import DataFromPlugins, DataToExport @@ -38,12 +38,11 @@ def __init__(self, parent=None, params_state=None, control_type="0D"): self.live = False self.control_type = control_type # could be "0D", "1D" or "Actuator" if self.control_type == "0D": - self.settings.child('NIDAQ_type').setLimits( - ['Analog_Input', 'Counter', 'Digital_Input']) # analog input and counter + self.settings.child('NIDAQ_type').setLimits(DAQ_NIDAQ_source.sources0D()) # analog input and counter elif self.control_type == "1D": - self.settings.child('NIDAQ_type').setLimits(['Analog_Input']) + self.settings.child('NIDAQ_type').setLimits(DAQ_NIDAQ_source.sources1D()) elif self.control_type == "Actuator": - self.settings.child('NIDAQ_type').setLimits(['Analog_Output']) + self.settings.child('NIDAQ_type').setLimits(DAQ_NIDAQ_source.Actuator()) self.settings.child('ao_settings').hide() self.settings.child('ao_channels').hide() @@ -105,10 +104,15 @@ def ini_detector(self, controller=None): # actions to perform in order to set properly the settings tree options self.commit_settings(self.settings.child('NIDAQ_type')) for ch in self.config_channels: + try: + ch.analog_type = ch.analog_type.upper() + ch.termination = ch.termination.upper() + except: + pass if self.settings.child("dev_to_use").value() in ch.name: self.settings.child('ai_channels').addNew(ch.name) param = [a for a in self.settings.child('ai_channels').childs if a.opts['title'] == ch.name][0] - self.settings.child("ai_channels", param.opts['name'], "ai_type").setValue(ch.analog_type) + self.settings.child("ai_channels", param.opts['name'], "ai_type").setValue(ch.analog_type.upper()) param.child("voltage_settings").show(ch.analog_type == UsageTypeAI.VOLTAGE.name) param.child("current_settings").show(ch.analog_type == UsageTypeAI.CURRENT.name) param.child("thermoc_settings").show(ch.analog_type == UsageTypeAI.TEMPERATURE_THERMOCOUPLE.name) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index 3e2efac..9326413 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -7,7 +7,7 @@ from nidaqmx.constants import AcquisitionType, VoltageUnits, CurrentUnits, CurrentShuntResistorLocation, \ TemperatureUnits, CJCSource, CountDirection, Level, FrequencyUnits, TimeUnits, \ LineGrouping -from . import UsageTypeAI, Edge, TerminalConfiguration, ThermocoupleType +from pymodaq_plugins_daqmx.hardware.national_instruments import UsageTypeAI, Edge, TerminalConfiguration, ThermocoupleType from nidaqmx.system import System as niSystem from nidaqmx.system.device import Device as niDevice @@ -58,6 +58,15 @@ class DAQ_NIDAQ_source(IntEnumExtend): Digital_Output = 4 Terminals = 5 + def sources0D(self): + return [self.Analog_Input.name, self.Counter.name, self.Digital_Input.name] + + def sources1D(self): + return [self.Analog_Input.name] + + def Actuator(self): + return [self.Analog_Output.name] + class ClockSettingsBase: def __init__(self, Nsamples=1000, repetition=False): @@ -275,7 +284,7 @@ def configuration_sequence(self, viewer, current_device): logger.info("Detected devices: {}".format(devices_info)) try: viewer.config_devices = [config["NIDAQ_Devices", dev].get('name') for dev in viewer.config["NIDAQ_Devices"] - if "Mod" not in config["NIDAQ_Devices", dev].get('name')] + if "Mod" not in config["NIDAQ_Devices", dev].get('name')] logger.info(viewer.config_devices) for dev in config["NIDAQ_Devices"]: if not isinstance(config["NIDAQ_Devices", dev], dict): @@ -319,6 +328,13 @@ def configuration_sequence(self, viewer, current_device): value_max=float(ai[ch].get("value_max")), termination=TerminalConfiguration.__getitem__(term), )) + (name=name, + source=ai[ch].get("source"), + analog_type=ai[ch].get("analog_type"), + value_min=float(ai[ch].get("value_min")), + value_max=float(ai[ch].get("value_max")), + termination=TerminalConfiguration.__getitem__(term), + )) elif ai[ch].get("analog_type") == UsageTypeAI.CURRENT.name: viewer.config_channels.append(AIChannel (name=name, From bf1df4be7ed016179fa555271aa4a26747022fe1 Mon Sep 17 00:00:00 2001 From: Loic GUILMARD Date: Fri, 20 Dec 2024 11:57:56 +0100 Subject: [PATCH 39/57] enums typos and mix-ups --- .../national_instruments/daq_NIDAQmx.py | 2 +- .../hardware/national_instruments/daqmxni.py | 21 ++++++++----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index 926f713..43c0d97 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -414,7 +414,7 @@ def update_task(self): enable=self.settings['trigger_settings', 'enable'], edge=Edge[self.settings['trigger_settings', 'edge'].upper()], level=self.settings['trigger_settings', 'level'], ) - if not not self.channels: + if self.channels: self.controller.update_task(self.channels, self.clock_settings, trigger_settings=self.trigger_settings) def get_channels_from_settings(self): diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index 9326413..aedea58 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -58,12 +58,15 @@ class DAQ_NIDAQ_source(IntEnumExtend): Digital_Output = 4 Terminals = 5 + @classmethod def sources0D(self): - return [self.Analog_Input.name, self.Counter.name, self.Digital_Input.name] + return [self.Analog_Input.name, DAQ_NIDAQ_source.Counter.name, DAQ_NIDAQ_source.Digital_Input.name] + @classmethod def sources1D(self): return [self.Analog_Input.name] + @classmethod def Actuator(self): return [self.Analog_Output.name] @@ -321,13 +324,6 @@ def configuration_sequence(self, viewer, current_device): term = ai[ch].get("termination") if ai[ch].get("analog_type") == UsageTypeAI.VOLTAGE.name: viewer.config_channels.append(AIChannel - (name=name, - source=ai[ch].get("source"), - analog_type=ai[ch].get("analog_type"), - value_min=float(ai[ch].get("value_min")), - value_max=float(ai[ch].get("value_max")), - termination=TerminalConfiguration.__getitem__(term), - )) (name=name, source=ai[ch].get("source"), analog_type=ai[ch].get("analog_type"), @@ -344,7 +340,8 @@ def configuration_sequence(self, viewer, current_device): value_max=float(ai[ch].get("value_max")), termination=TerminalConfiguration.__getitem__(term), )) - elif ai[ch].get("analog_type") == UsageTypeAI.TEMPERATURE_THERMOCOUPLE.name: + elif ai[ch].get("analog_type") == UsageTypeAI.TEMPERATURE_THERMOCOUPLE.name or \ + ai[ch].get("analog_type") == "Thermocouple": th = ai[ch].get("thermo_type") viewer.config_channels.append(AIThermoChannel (name=name, @@ -415,7 +412,7 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti # create all channels one task for one type of channels for channel in channels: - if channel.source == DAQ_NIDAQ_source.Digital_Input.name: # analog input + if channel.source == DAQ_NIDAQ_source.Analog_Input.name: # analog input try: if channel.analog_type == UsageTypeAI.VOLTAGE.name: self._task.ai_channels.add_ai_voltage_chan(channel.name, @@ -437,7 +434,7 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti 0., "") - elif channel.analog_type == UsageTypeAI.CURRENT.name: + elif channel.analog_type == UsageTypeAI.TEMPERATURE_THERMOCOUPLE.name: self._task.ai_channels.add_ai_thrmcpl_chan(channel.name, "", channel.value_min, @@ -449,7 +446,7 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti "") except DaqError as e: err_code = e.error_code - if not not err_code: + if err_code: status = self.DAQmxGetErrorString(err_code) raise IOError(status) elif channel.source == 'Counter': # counter From 7db15965ea0bbf8d906392a43daf9dd31cee63c7 Mon Sep 17 00:00:00 2001 From: Loic GUILMARD Date: Fri, 20 Dec 2024 11:58:10 +0100 Subject: [PATCH 40/57] Added aenum to dependencies --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 329457c..3726a60 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,8 @@ description = 'Hardware plugins for PyMoDAQ using the NiDAQmx framework (nidaq w dependencies = [ "pymodaq>=4.4", "pydaqmx", - "nidaqmx" + "nidaqmx", + "aenum" ] authors = [ From 004e910e3455b4adc7433a8cec09c9047fd8dfa3 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Tue, 7 Jan 2025 10:51:11 +0100 Subject: [PATCH 41/57] Fix viewer & move double implementation --- .../national_instruments/daq_NIDAQmx.py | 405 +----------------- .../national_instruments/daq_NIDAQmx_Move.py | 12 +- 2 files changed, 6 insertions(+), 411 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index 43c0d97..61b6443 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -1,18 +1,11 @@ from qtpy import QtWidgets -from qtpy.QtCore import Signal, QTimer -import numpy as np +from qtpy.QtCore import Signal from pymodaq.utils.logger import set_logger, get_module_name -from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters as viewer_params -from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, comon_parameters as actuator_params -import traceback from pymodaq.utils.parameter import Parameter from pymodaq.utils.parameter.pymodaq_ptypes import registerParameterType, GroupParameter from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import DAQmx, Edge, DAQ_NIDAQ_source, ClockSettings, \ AIChannel, Counter, AIThermoChannel, AOChannel, TriggerSettings, DOChannel, DIChannel, UsageTypeAI, \ ThermocoupleType, TerminalConfiguration -from pymodaq.utils.daq_utils import ThreadCommand, getLineInfo -from pymodaq.utils.data import DataToExport, DataFromPlugins, DataActuator -from easydict import EasyDict as eDict logger = set_logger(get_module_name(__file__)) @@ -479,400 +472,4 @@ def stop(self): self.controller.stop() -class DAQ_NIDAQmx_Viewer(DAQ_Viewer_base, DAQ_NIDAQmx_base): - """ - ==================== ======================== - **Attributes** **Type** - *data_grabed_signal* instance of Signal - *params* dictionnary list - *task* - ==================== ======================== - - See Also - -------- - refresh_hardware - """ - - live_mode_available = True - params = viewer_params + DAQ_NIDAQmx_base.params - - def __init__(self, parent=None, params_state=None, control_type="0D"): - DAQ_Viewer_base.__init__(self, parent, params_state) # defines settings attribute and various other methods - DAQ_NIDAQmx_base.__init__(self) - - self.Naverage = None - self.live = False - self.control_type = control_type # could be "0D", "1D" or "Actuator" - if self.control_type == "0D": - self.settings.child('NIDAQ_type').setLimits(DAQ_NIDAQ_source.Source0D()) # analog input and counter - elif self.control_type == "1D": - self.settings.child('NIDAQ_type').setLimits([DAQ_NIDAQ_source.sources1D()]) - elif self.control_type == "Actuator": - self.settings.child('NIDAQ_type').setLimits([DAQ_NIDAQ_source.Actuator()]) - - self.settings.child('ao_settings').hide() - self.settings.child('ao_channels').hide() - - # timer used for the counter - self.timer = QTimer() - self.timer.setSingleShot(True) - self.timer.timeout.connect(self.counter_done) - - def stop(self): - """Stop the current grab hardware wise if necessary""" - self.controller.stop() - self.live = False - self.emit_status(ThreadCommand('Update_Status', ['Acquisition stopped.'])) - return '' - - def commit_settings(self, param): - """ - Activate the parameters changes in the hardware. - - =============== ================================ =========================== - **Parameters** **Type** **Description** - *param* instance of pyqtgraph.parameter the parameter to activate - =============== ================================ =========================== - - See Also - -------- - update_NIDAQ_channels, update_task, DAQ_NIDAQ_source, refresh_hardware - """ - - if param.parent() is not None: - if param.parent().name() == 'ai_channels': - device = param.opts['title'].split('/')[0] - self.settings.child('clock_settings', 'frequency').setOpts(max=self.getAIMaxRate(device)) - - ranges = self.controller.getAIVoltageRange(device) - param.child('voltage_settings', 'volt_min').setOpts(limits=[r[0] for r in ranges]) - param.child('voltage_settings', 'volt_max').setOpts(limits=[r[1] for r in ranges]) - - DAQ_NIDAQmx_base.commit_settings(self, param) - - def ini_detector(self, controller=None): - """ - Initialisation procedure of the detector. - - See Also - -------- - daq_utils.ThreadCommand - """ - try: - self.controller = self.ini_detector_init(controller, DAQmx()) - self.update_task() - - # actions to perform in order to set properly the settings tree options - self.commit_settings(self.settings.child('NIDAQ_type')) - - info = "Plugin Initialized" - initialized = True - return info, initialized - - except Exception as e: - logger.info(traceback.format_exc()) - self.emit_status(ThreadCommand('Update_Status', [str(e), 'log'])) - info = str(e) - initialized = False - return info, initialized - - def grab_data(self, Naverage=1, **kwargs): - """ - | grab the current values with NIDAQ profile procedure. - | - | Send the data_grabed_signal once done. - - =============== ======== =============================================== - **Parameters** **Type** **Description** - *Naverage* int Number of values to average - =============== ======== =============================================== - - See Also - -------- - DAQ_NIDAQ_source - """ - update = False - - if 'live' in kwargs: - if kwargs['live'] != self.live: - update = True - self.live = kwargs['live'] - - if Naverage != self.Naverage: - self.Naverage = Naverage - update = True - if update: - self.update_task() - - if self.controller.task is None: - self.update_task() - - self.controller.register_callback(self.emit_data, "Nsamples", self.clock_settings.Nsamples) - self.controller.start() - - def emit_data(self, task_handle, every_n_samples_event_type, number_of_samples, callback_data): - channels_names = [ch.name for ch in self.channels] - # channels_ai_names = [ch.name for ch in self.channels if ch.source == 'Analog_Input'] - data_from_task = self.controller.task.read(self.settings['nsamplestoread'], timeout=20.0) - if self.control_type == "0D": - if not len(self.controller.task.channels.channel_names) != 1: - data_dfp = [np.array(data_from_task)] - else: - data_dfp = list(map(np.array, data_from_task)) - dte = DataToExport(name='NIDAQmx', - data=[DataFromPlugins(name='NI Analog Input', - data=data_dfp, - dim=f'Data{self.settings.child("display").value()}', - labels=channels_names - ), - ]) - self.dte_signal.emit(dte) - return 0 # mandatory for the NIDAQmx callback - - def counter_done(self): - channels_name = [ch.name for ch in self.channels] - data_counter = self.readCounter(len(self.channels), - self.settings['counter_settings', 'counting_time'] * 1e-3) - self.data_grabed_signal.emit([DataFromPlugins(name='NI Counter', data=[data_counter / 1e-3], dim='Data0D', - labels=channels_name, )]) - # y_axis=Axis(label='Count Number', units='1/s'))]) - self.task.StopTask() - - -class DAQ_NIDAQmx_Actuator(DAQ_Move_base, DAQ_NIDAQmx_base): - """ - Wrapper object to access the Mock fonctionnalities, similar wrapper for all controllers. - - =============== ============== - **Attributes** **Type** - *params* dictionnary - =============== ============== - """ - _controller_units = 'Volts' - is_multiaxes = False # set to True if this plugin is controlled for a multiaxis controller (with a unique communication link) - stage_names = [] # "list of strings of the multiaxes - - params = DAQ_NIDAQmx_base.params + [ - # elements to be added here as dicts in order to control your custom stage - ############ - {'title': 'MultiAxes:', 'name': 'multiaxes', 'type': 'group', 'visible': is_multiaxes, - 'children': [ - {'title': 'is Multiaxes:', 'name': 'ismultiaxes', 'type': 'bool', 'value': is_multiaxes, - 'default': False}, - {'title': 'Status:', 'name': 'multi_status', 'type': 'list', 'value': 'Master', - 'limits': ['Master', 'Slave']}, - {'title': 'Axis:', 'name': 'axis', 'type': 'list', 'limits': stage_names}, - ]}] + actuator_params() - - def __init__(self, parent=None, params_state=None, control_type="Actuator"): - DAQ_Move_base.__init__(self, parent, params_state) # defines settings attribute and various other methods - DAQ_NIDAQmx_base.__init__(self) - - self.control_type = "Actuator" # could be "0D", "1D" or "Actuator" - self.settings.child('NIDAQ_type').setLimits(['Analog_Output', 'Digital_Output']) - - self.settings.child('clock_settings', 'Nsamples').setValue(1) - - def get_actuator_value(self) -> DataActuator: - """Get the current position from the hardware with scaling conversion. - - Returns - ------- - float: The position obtained after scaling conversion. - """ - - pos = self.target_position - ## - - pos = self.get_position_with_scaling(pos) - self.emit_status(ThreadCommand('check_position', [pos])) - return pos - - def commit_settings(self, param): - """ - | Activate any parameter changes on the PI_GCS2 hardware. - | - | Called after a param_tree_changed signal from DAQ_Move_main. - - """ - - DAQ_NIDAQmx_base.commit_settings(self, param) - if param.name() == 'waveform': - if param.value() == 'DC': - self.settings.child('ao_settings', 'cont_param').setValue('offset') - self.settings.child('ao_settings', 'cont_param').show(not param.value() == 'DC') - self.settings.child('ao_settings', 'waveform_settings').show(not param.value() == 'DC') - - if param.parent() is not None: - if param.parent().name() == 'ao_channels': - device = param.opts['title'].split('/')[0] - self.settings.child('clock_settings', 'frequency').setOpts(max=self.getAOMaxRate(device)) - - ranges = self.getAOVoltageRange(device) - param.child('voltage_settings', 'volt_min').setOpts(limits=[r[0] for r in ranges]) - param.child('voltage_settings', 'volt_max').setOpts(limits=[r[1] for r in ranges]) - - def ini_stage(self, controller=None): - """Actuator communication initialization - - Parameters - ---------- - controller: (object) custom object of a PyMoDAQ plugin (Slave case). None if only one actuator by controller (Master case) - - Returns - ------- - self.status (easydict): with initialization status: three fields: - * info (str) - * controller (object) initialized controller - *initialized: (bool): False if initialization failed otherwise True - """ - - try: - # initialize the stage and its controller status - # controller is an object that may be passed to other instances of DAQ_Move_Mock in case - # of one controller controlling multiactuators (or detector) - - self.status.update(eDict(info="", controller=None, initialized=False)) - - # check whether this stage is controlled by a multiaxe controller (to be defined for each plugin) - # if multiaxes then init the controller here if Master state otherwise use external controller - if self.settings['multiaxes', 'ismultiaxes'] and self.settings['multiaxes', - 'multi_status'] == "Slave": - if controller is None: - raise Exception('no controller has been defined externally while this axe is a slave one') - else: - self.controller = controller - else: - self.controller = 'A Nidaqmx task' - self.update_task() - - # actions to perform in order to set properly the settings tree options - self.commit_settings(self.settings.child('NIDAQ_type')) - - self.status.info = "Plugin Initialized" - self.status.controller = self.controller - self.status.initialized = True - return self.status - - except Exception as e: - self.emit_status(ThreadCommand('Update_Status', [getLineInfo() + str(e), 'log'])) - self.status.info = getLineInfo() + str(e) - self.status.initialized = False - return self.status - - def calulate_waveform(self, value): - waveform = self.settings['ao_settings', 'waveform'] - if waveform == 'DC': - values = np.array([value]) - else: - Nsamples = self.settings['clock_settings', 'Nsamples'] - freq = self.settings['clock_settings', 'frequency'] - time = np.linspace(0, Nsamples / freq, Nsamples, endpoint=False) - - freq0 = self.settings['ao_settings', 'waveform_settings', 'frequency'] - amp = self.settings['ao_settings', 'waveform_settings', 'amplitude'] - offset = self.settings['ao_settings', 'waveform_settings', 'offset'] - if waveform == 'Sinus': - values = offset + amp * np.sin(2 * np.pi * freq0 * time) - elif waveform == 'Ramp': - values = offset + amp * np.linspace(0, 1, Nsamples) - - return values - - def move_abs(self, position): - """ Move the actuator to the absolute target defined by position - - Parameters - ---------- - position: (flaot) value of the absolute target positioning - """ - - position = self.check_bound(position) # if user checked bounds, the defined bounds are applied here - position = self.set_position_with_scaling(position) # apply scaling if the user specified one - if self.settings['NIDAQ_type'] == 'Analog_Output': - self.settings.child('ao_settings', 'waveform_settings', - self.settings['ao_settings', 'cont_param']).setValue(position) - values = self.calulate_waveform(position) - self.target_position = position - - self.stop() - - if len(values) == 1: - self.writeAnalog(len(values), 1, values, autostart=True) - self.current_position = self.check_position() - self.move_done() - else: - if self.c_callback is None: - self.register_callback(self.move_done_callback) - self.writeAnalog(len(values), 1, values, autostart=False) - self.task.StartTask() - elif self.settings['NIDAQ_type'] == 'Digital_Output': - self.writeDigital(1, np.array([position], dtype=np.uint8), autostart=True) - - def move_done_callback(self, taskhandle, status, callbackdata): - self.current_position = self.check_position() - QtWidgets.QApplication.processEvents() - self.move_done() - self.task.StopTask() - return 0 - - def move_rel(self, position): - """ Move the actuator to the relative target actuator value defined by position - - Parameters - ---------- - position: (flaot) value of the relative target positioning - """ - - position = self.check_bound(self.current_position + position) - self.current_position - self.target_position = position + self.current_position - if self.settings['NIDAQ_type'] == 'Analog_Output': - self.settings.child('ao_settings', 'waveform_settings', - self.settings['ao_settings', 'cont_param']).setValue(self.target_position) - - values = self.calulate_waveform(self.target_position) - - self.stop() - - if len(values) == 1: - self.writeAnalog(len(values), 1, values, autostart=True) - self.current_position = self.check_position() - self.move_done() - else: - if self.c_callback is None: - self.register_callback(self.move_done_callback) - self.writeAnalog(len(values), 1, values, autostart=False) - self.task.StartTask() - elif self.settings['NIDAQ_type'] == 'Digital_Output': - self.writeDigital(1, np.array([self.target_position], dtype=np.uint8), autostart=True) - - def move_home(self): - """ - Send the update status thread command. - See Also - -------- - daq_utils.ThreadCommand - """ - - self.stop() - - if self.c_callback is None: - self.register_callback(self.move_done_callback) - self.writeAnalog(1, 1, np.array([0.])) - self.task.StartTask() - - def stop_motion(self): - """ - Call the specific move_done function (depending on the hardware). - - See Also - -------- - move_done - """ - - ## TODO for your custom plugin - self.stop() - self.emit_status(ThreadCommand('Update_Status', ['Some info you want to log'])) - self.move_done() # to let the interface know the actuator stopped - diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Move.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Move.py index 35d699c..d38ca0b 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Move.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Move.py @@ -32,8 +32,7 @@ class DAQ_NIDAQmx_Actuator(DAQ_Move_base, DAQ_NIDAQmx_base): {'title': 'Status:', 'name': 'multi_status', 'type': 'list', 'value': 'Master', 'limits': ['Master', 'Slave']}, {'title': 'Axis:', 'name': 'axis', 'type': 'list', 'limits': stage_names}, - - ]}] + actuator_params + ]}] + actuator_params() def __init__(self, parent=None, params_state=None, control_type="Actuator"): DAQ_Move_base.__init__(self, parent, params_state) # defines settings attribute and various other methods @@ -92,7 +91,7 @@ def ini_stage(self, controller=None): Returns ------- - self.status (edict): with initialization status: three fields: + self.status (easydict): with initialization status: three fields: * info (str) * controller (object) initialized controller *initialized: (bool): False if initialization failed otherwise True @@ -150,7 +149,7 @@ def calulate_waveform(self, value): return values - def move_Abs(self, position): + def move_abs(self, position): """ Move the actuator to the absolute target defined by position Parameters @@ -187,7 +186,7 @@ def move_done_callback(self, taskhandle, status, callbackdata): self.task.StopTask() return 0 - def move_Rel(self, position): + def move_rel(self, position): """ Move the actuator to the relative target actuator value defined by position Parameters @@ -217,7 +216,7 @@ def move_Rel(self, position): elif self.settings['NIDAQ_type'] == 'Digital_Output': self.writeDigital(1, np.array([self.target_position], dtype=np.uint8), autostart=True) - def move_Home(self): + def move_home(self): """ Send the update status thread command. See Also @@ -245,4 +244,3 @@ def stop_motion(self): self.stop() self.emit_status(ThreadCommand('Update_Status', ['Some info you want to log'])) self.move_done() # to let the interface know the actuator stopped - ############################## From 90c72b52302ea6d5435dbf6ce343fb80fd255f0e Mon Sep 17 00:00:00 2001 From: Sebastien Date: Thu, 9 Jan 2025 11:20:20 +0100 Subject: [PATCH 42/57] Remove use of deprecated .members() method --- .../hardware/national_instruments/daqmxni.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index aedea58..0b12562 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -82,7 +82,7 @@ class ClockSettings(ClockSettingsBase): def __init__(self, source=None, frequency=1000, Nsamples=1000, edge=Edge.RISING, repetition=False): super().__init__(Nsamples, repetition) self.source = source - assert edge in Edge.members() + assert edge in Edge self.frequency = frequency self.edge = edge @@ -97,7 +97,7 @@ def __init__(self, Nsamples=1000, rising_channel='', falling_channel='', class TriggerSettings: def __init__(self, trig_source='', enable=False, edge=Edge.RISING, level=0.1): - assert edge in Edge.members() + assert edge in Edge self.trig_source = trig_source self.enable = enable self.edge = edge @@ -133,14 +133,14 @@ def __init__(self, analog_type=UsageTypeAI.VOLTAGE, value_min=-10., value_max=+1 class AIChannel(AChannel): def __init__(self, termination=TerminalConfiguration.DEFAULT, **kwargs): super().__init__(**kwargs) - assert termination in TerminalConfiguration.members() + assert termination in TerminalConfiguration self.termination = termination class AIThermoChannel(AIChannel): def __init__(self, thermo_type=ThermocoupleType.K, **kwargs): super().__init__(**kwargs) - assert thermo_type in ThermocoupleType.members() + assert thermo_type in ThermocoupleType self.thermo_type = thermo_type @@ -151,7 +151,7 @@ def __init__(self, **kwargs): class Counter(Channel): def __init__(self, edge=Edge.RISING, **kwargs): - assert edge in Edge.members() + assert edge in Edge super().__init__(**kwargs) self.edge = edge self.counter_type = "Edge Counter" From 8ec8fe2e46dc3b89c7ec9c01032940720da351e7 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Fri, 10 Jan 2025 15:57:15 +0100 Subject: [PATCH 43/57] [Cleaning] Useless infos removed, spaces & main --- .../plugins_0D/daq_0Dviewer_NIDAQmx.py | 2 -- .../national_instruments/daq_NIDAQmx.py | 2 +- .../hardware/national_instruments/daqmxni.py | 17 ++--------------- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py index 94fd711..575c76b 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py @@ -32,8 +32,6 @@ class DAQ_0DViewer_NIDAQmx(DAQ_NIDAQmx_Viewer): {'title': 'Devices :', 'name': 'devices', 'type': 'list', 'limits': param_devices, 'value': param_devices[0] }, - {'title': 'Device To Use:', 'name': 'dev_to_use', 'type': 'list', 'limits': param_devices, - }, ] + DAQ_NIDAQmx_base.params def __init__(self, parent=None, params_state=None): diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index 61b6443..290c45d 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -460,7 +460,7 @@ def get_channels_from_settings(self): channels.append(DOChannel(name=channel.opts['title'], source=DAQ_NIDAQ_source.Digital_Output.name)) - channels = [ch for ch in channels if self.settings.child("dev_to_use").value() in ch.name] + channels = [ch for ch in channels if self.settings.child("devices").value() in ch.name] return channels def stop(self): diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index 0b12562..c64c8b6 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -288,7 +288,6 @@ def configuration_sequence(self, viewer, current_device): try: viewer.config_devices = [config["NIDAQ_Devices", dev].get('name') for dev in viewer.config["NIDAQ_Devices"] if "Mod" not in config["NIDAQ_Devices", dev].get('name')] - logger.info(viewer.config_devices) for dev in config["NIDAQ_Devices"]: if not isinstance(config["NIDAQ_Devices", dev], dict): continue @@ -352,9 +351,8 @@ def configuration_sequence(self, viewer, current_device): thermo_type=ThermocoupleType.__getitem__(th), )) logger.info("Devices from config: {}".format(viewer.config_devices)) - logger.info("Current device: {}".format(current_device)) - logger.info("Current device modules from config: {}".format(viewer.config_modules)) - logger.info("Current device channels from config: {}".format([ch.name for ch in viewer.config_channels])) + logger.info("Modules from config: {}".format(viewer.config_modules)) + logger.info("Channels from config: {}".format([ch.name for ch in viewer.config_channels])) except AssertionError as err: logger.error("Configuration entries <{}> does not match the hardware ".format(err)) except Exception as err: @@ -644,15 +642,4 @@ def refresh_hardware(self): if __name__ == '__main__': - print(DAQmx.get_NIDAQ_channels()) - controller = DAQmx() - ch_counter = Counter(name='Dev1/ctr0', - source='Counter', - edge=Edge.members()[0]) - controller.update_task([ch_counter]) - controller.start() - print(controller.readCounter()) - controller.stop() - controller.close() - pass From 750db1bffe2894cf4aff5b4c85a79bf865afafd6 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Fri, 17 Jan 2025 14:29:50 +0100 Subject: [PATCH 44/57] [Optimizing] nidaqmx enum constants in their simplest use + channels added more efficiently + small cleaning of useless info or methods --- .../hardware/national_instruments/__init__.py | 47 -------- .../national_instruments/daq_NIDAQmx.py | 79 ++++++------- .../daq_NIDAQmx_Viewer.py | 22 ++-- .../hardware/national_instruments/daqmxni.py | 106 +++++++----------- 4 files changed, 90 insertions(+), 164 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/__init__.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/__init__.py index 987567a..e69de29 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/__init__.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/__init__.py @@ -1,47 +0,0 @@ -from aenum import extend_enum -from types import MethodType -from nidaqmx.constants import UsageTypeAI, Edge, TerminalConfiguration, ThermocoupleType - - -def members(classref): - """ - Return the list of the members of cls.__members__.items() - """ - return list(classref._member_map_.values()) - - -def names(classref): - """ - Return the list of the names of the enum members - """ - return classref._member_names_ - - -extend_enum(UsageTypeAI, "Thermocouple", UsageTypeAI.TEMPERATURE_THERMOCOUPLE.value) - - -extend_enum(TerminalConfiguration, "Auto", TerminalConfiguration.DEFAULT.value) - - -UsageTypeAI.members = MethodType(members, UsageTypeAI) - - -UsageTypeAI.names = MethodType(names, UsageTypeAI) - - -Edge.members = MethodType(members, Edge) - - -Edge.names = MethodType(names, Edge) - - -TerminalConfiguration.members = MethodType(members, TerminalConfiguration) - - -TerminalConfiguration.names = MethodType(names, TerminalConfiguration) - - -ThermocoupleType.members = MethodType(members, ThermocoupleType) - - -ThermocoupleType.names = MethodType(names, ThermocoupleType) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index 290c45d..d49d032 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -4,7 +4,7 @@ from pymodaq.utils.parameter import Parameter from pymodaq.utils.parameter.pymodaq_ptypes import registerParameterType, GroupParameter from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import DAQmx, Edge, DAQ_NIDAQ_source, ClockSettings, \ - AIChannel, Counter, AIThermoChannel, AOChannel, TriggerSettings, DOChannel, DIChannel, UsageTypeAI, \ + AIChannel, Counter, AIThermoChannel, AOChannel, TriggerSettings, DOChannel, DIChannel, UsageTypeAI, UsageTypeAO, \ ThermocoupleType, TerminalConfiguration @@ -26,7 +26,7 @@ class ScalableGroupAI(GroupParameter): hardware.DAQ_Move_Stage_type """ - params = [{'title': 'AI type:', 'name': 'ai_type', 'type': 'list', 'limits': UsageTypeAI.names()}, + params = [{'title': 'AI type:', 'name': 'ai_type', 'type': 'list', 'limits': [Uai.name for Uai in UsageTypeAI]}, {'title': 'Voltage:', 'name': 'voltage_settings', 'type': 'group', 'children': [ {'title': 'Voltage Min:', 'name': 'volt_min', 'type': 'float', 'value': -10.}, {'title': 'Voltage Max:', 'name': 'volt_max', 'type': 'float', 'value': 10.}, @@ -36,12 +36,13 @@ class ScalableGroupAI(GroupParameter): {'title': 'Current Max:', 'name': 'curr_max', 'type': 'float', 'value': 1, 'suffix': 'A'}, ]}, {'title': 'Thermocouple:', 'name': 'thermoc_settings', 'type': 'group', 'visible': False, 'children': [ - {'title': 'Thc. type:', 'name': 'thermoc_type', 'type': 'list', 'limits': ThermocoupleType.names(), - 'value': 'K'}, + {'title': 'Thc. type:', 'name': 'thermoc_type', 'type': 'list', + 'limits': [Th.name for Th in ThermocoupleType], 'value': 'K'}, {'title': 'Temp. Min (°C):', 'name': 'T_min', 'type': 'float', 'value': 0, 'suffix': '°C'}, {'title': 'Temp. Max (°C):', 'name': 'T_max', 'type': 'float', 'value': 50, 'suffix': '°C'}, ]}, - {'title': 'Termination:', 'name': 'termination', 'type': 'list', 'limits': TerminalConfiguration.names()}, + {'title': 'Termination:', 'name': 'termination', 'type': 'list', + 'limits': [Te.name for Te in TerminalConfiguration]}, ] def __init__(self, **opts): @@ -88,7 +89,7 @@ class ScalableGroupAO(GroupParameter): hardware.DAQ_Move_Stage_type """ - params = [{'title': 'AO type:', 'name': 'ao_type', 'type': 'list', 'limits': UsageTypeAI.names()[0:2]}, + params = [{'title': 'AO type:', 'name': 'ao_type', 'type': 'list', 'limits': [Uao.name for Uao in UsageTypeAO]}, {'title': 'Voltages:', 'name': 'voltage_settings', 'type': 'group', 'children': [ {'title': 'Voltage Min:', 'name': 'volt_min', 'type': 'list', 'value': -10., }, {'title': 'Voltage Max:', 'name': 'volt_max', 'type': 'list', 'value': 10., }, @@ -143,7 +144,7 @@ class ScalableGroupCounter(GroupParameter): hardware.DAQ_Move_Stage_type """ - params = [{'title': 'Edge type:', 'name': 'edge', 'type': 'list', 'limits': Edge.names()}, ] + params = [{'title': 'Edge type:', 'name': 'edge', 'type': 'list', 'limits': [e.name for e in Edge]}, ] def __init__(self, **opts): opts['type'] = 'groupcounter' @@ -252,7 +253,8 @@ class DAQ_NIDAQmx_base: data_grabed_signal = Signal(list) param_instance = DAQmx() params = [{'title': 'Refresh hardware:', 'name': 'refresh_hardware', 'type': 'bool', 'value': False}, - {'title': 'Signal type:', 'name': 'NIDAQ_type', 'type': 'list', 'limits': DAQ_NIDAQ_source.names()}, + {'title': 'Signal type:', 'name': 'NIDAQ_type', 'type': 'list', + 'limits': [Ds.name for Ds in DAQ_NIDAQ_source]}, {'title': 'NSamples To Read', 'name': 'nsamplestoread', 'type': 'int', 'value': 1000, 'default': 1000, 'min': 1}, {'title': 'AO Settings:', 'name': 'ao_settings', 'type': 'group', 'children': [ @@ -275,24 +277,25 @@ class DAQ_NIDAQmx_base: ] }, {'title': 'AI Channels:', 'name': 'ai_channels', 'type': 'groupai', - 'limits': DAQmx.get_NIDAQ_channels(source_type=DAQ_NIDAQ_source.Analog_Input.name)}, + 'limits': DAQmx.get_NIDAQ_channels(source_type=DAQ_NIDAQ_source.Analog_Input)}, {'title': 'AO Channels:', 'name': 'ao_channels', 'type': 'groupao', - 'limits': DAQmx.get_NIDAQ_channels(source_type=DAQ_NIDAQ_source.Analog_Output.name)}, + 'limits': DAQmx.get_NIDAQ_channels(source_type=DAQ_NIDAQ_source.Analog_Output)}, {'title': 'DO Channels:', 'name': 'do_channels', 'type': 'groupdo', - 'limits': DAQmx.get_NIDAQ_channels(source_type=DAQ_NIDAQ_source.Digital_Output.name)}, + 'limits': DAQmx.get_NIDAQ_channels(source_type=DAQ_NIDAQ_source.Digital_Output)}, {'title': 'DI Channels:', 'name': 'di_channels', 'type': 'groupdi', - 'limits': DAQmx.get_NIDAQ_channels(source_type=DAQ_NIDAQ_source.Digital_Input.name)}, + 'limits': DAQmx.get_NIDAQ_channels(source_type=DAQ_NIDAQ_source.Digital_Input)}, {'title': 'Counter Settings:', 'name': 'counter_settings', 'type': 'group', 'visible': True, 'children': [ {'title': 'Counting time (ms):', 'name': 'counting_time', 'type': 'float', 'value': 100., 'default': 100., 'min': 0.}, {'title': 'Counting Channels:', 'name': 'counter_channels', 'type': 'groupcounter', - 'limits': DAQmx.get_NIDAQ_channels(source_type=DAQ_NIDAQ_source.Counter.name)}, + 'limits': DAQmx.get_NIDAQ_channels(source_type=DAQ_NIDAQ_source.Counter)}, ]}, {'title': 'Trigger Settings:', 'name': 'trigger_settings', 'type': 'group', 'visible': True, 'children': [ {'title': 'Enable?:', 'name': 'enable', 'type': 'bool', 'value': False, }, {'title': 'Trigger Source:', 'name': 'trigger_channel', 'type': 'list', 'limits': DAQmx.getTriggeringSources()}, - {'title': 'Edge type:', 'name': 'edge', 'type': 'list', 'limits': Edge.names(), 'visible': False}, + {'title': 'Edge type:', 'name': 'edge', 'type': 'list', 'limits': [e.name for e in Edge], + 'visible': False}, {'title': 'Level:', 'name': 'level', 'type': 'float', 'value': 1., 'visible': False} ]} ] @@ -321,7 +324,6 @@ def commit_settings(self, param: Parameter): """ if param.name() == 'NIDAQ_devices': self.controller.update_NIDAQ_channels() - self.update_task() if param.name() == 'NIDAQ_type': self.controller.update_NIDAQ_channels(param.value()) @@ -370,8 +372,6 @@ def commit_settings(self, param: Parameter): self.settings.child('do_channels').show() self.settings.child('di_channels').hide() - self.update_task() - elif param.name() == 'refresh_hardware': if param.value(): self.controller.refresh_hardware() @@ -379,25 +379,19 @@ def commit_settings(self, param: Parameter): self.settings.child('refresh_hardware').setValue(False) elif param.name() == 'ai_type': - param.parent().child('voltage_settings').show(param.value() == UsageTypeAI.Voltage.name) - param.parent().child('current_settings').show(param.value() == UsageTypeAI.Current.name) + param.parent().child('voltage_settings').show(param.value() == UsageTypeAI.VOLTAGE.name) + param.parent().child('current_settings').show(param.value() == UsageTypeAI.CURRENT.name) param.parent().child('thermoc_settings').show(param.value() == UsageTypeAI.TEMPERATURE_THERMOCOUPLE.name) - self.update_task() elif param.name() == 'ao_type': - param.parent().child('voltage_settings').show(param.value() == UsageTypeAI.Voltage.name) - param.parent().child('current_settings').show(param.value() == UsageTypeAI.Current.name) - self.update_task() + param.parent().child('voltage_settings').show(param.value() == UsageTypeAI.VOLTAGE.name) + param.parent().child('current_settings').show(param.value() == UsageTypeAI.CURRENT.name) elif param.name() == 'trigger_channel': param.parent().child('level').show('PF' not in param.opts['title']) - else: - self.update_task() - def update_task(self): self.channels = self.get_channels_from_settings() - logger.info("update_task - channels: {}".format(self.channels)) self.clock_settings = ClockSettings(frequency=self.settings['clock_settings', 'frequency'], Nsamples=self.settings['clock_settings', 'Nsamples'], edge=Edge.RISING, @@ -405,7 +399,7 @@ def update_task(self): self.trigger_settings = \ TriggerSettings(trig_source=self.settings['trigger_settings', 'trigger_channel'], enable=self.settings['trigger_settings', 'enable'], - edge=Edge[self.settings['trigger_settings', 'edge'].upper()], + edge=Edge[self.settings['trigger_settings', 'edge']], level=self.settings['trigger_settings', 'level'], ) if self.channels: self.controller.update_task(self.channels, self.clock_settings, trigger_settings=self.trigger_settings) @@ -414,23 +408,22 @@ def get_channels_from_settings(self): channels = [] if self.settings['NIDAQ_type'] == DAQ_NIDAQ_source.Analog_Input.name: # analog input for channel in self.settings.child('ai_channels').children(): - logger.info("get_channels_from_settings - channel {}".format(channel)) - analog_type = channel['ai_type'] # todo UsageTypeAI[channel['ai_type'].upper()].name - if analog_type == UsageTypeAI.VOLTAGE.name: + analog_type = UsageTypeAI[channel['ai_type']] + if analog_type == UsageTypeAI.VOLTAGE: channels.append(AIChannel(name=channel.opts['title'], - source=DAQ_NIDAQ_source.Analog_Input.name, analog_type=analog_type, + source=DAQ_NIDAQ_source.Analog_Input, analog_type=analog_type, value_min=channel['voltage_settings', 'volt_min'], value_max=channel['voltage_settings', 'volt_max'], - termination=TerminalConfiguration[channel['termination'].upper()], )) - elif analog_type == UsageTypeAI.CURRENT.name: + termination=TerminalConfiguration[channel['termination']], )) + elif analog_type == UsageTypeAI.CURRENT: channels.append(AIChannel(name=channel.opts['title'], - source=DAQ_NIDAQ_source.Analog_Input.name, analog_type=analog_type, + source=DAQ_NIDAQ_source.Analog_Input, analog_type=analog_type, value_min=channel['current_settings', 'curr_min'], value_max=channel['current_settings', 'curr_max'], - termination=TerminalConfiguration[channel['termination'].upper()], )) - elif analog_type == UsageTypeAI.TEMPERATURE_THERMOCOUPLE.name: + termination=TerminalConfiguration[channel['termination']], )) + elif analog_type == UsageTypeAI.TEMPERATURE_THERMOCOUPLE: channels.append(AIThermoChannel(name=channel.opts['title'], - source=DAQ_NIDAQ_source.Analog_Input.name, analog_type=analog_type, + source=DAQ_NIDAQ_source.Analog_Input, analog_type=analog_type, value_min=channel['thermoc_settings', 'T_min'], value_max=channel['thermoc_settings', 'T_max'], thermo_type=ThermocoupleType[ @@ -438,9 +431,9 @@ def get_channels_from_settings(self): elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source.Analog_Output.name: # analog output for channel in self.settings.child('ao_channels').children(): - analog_type = channel['ao_type'] + analog_type = UsageTypeAO[channel['ao_type']] channels.append(AOChannel(name=channel.opts['title'], - source=DAQ_NIDAQ_source.Analog_Output.name, analog_type=analog_type, + source=DAQ_NIDAQ_source.Analog_Output, analog_type=analog_type, value_min=channel['voltage_settings', 'volt_min'], value_max=channel['voltage_settings', 'volt_max'], )) @@ -448,17 +441,17 @@ def get_channels_from_settings(self): elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source.Counter.name: # counter input for channel in self.settings.child('counter_settings', 'counter_channels').children(): channels.append(Counter(name=channel.opts['title'], - source='Counter', edge=channel['edge'])) + source=DAQ_NIDAQ_source.Counter, edge=Edge[channel['edge']])) elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source.Digital_Input.name: # digital input for channel in self.settings.child('di_channels').children(): channels.append(DIChannel(name=channel.opts['title'], - source=DAQ_NIDAQ_source.Digital_Input.name)) + source=DAQ_NIDAQ_source.Digital_Input)) elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source.Digital_Output.name: # Digital output for channel in self.settings.child('do_channels').children(): channels.append(DOChannel(name=channel.opts['title'], - source=DAQ_NIDAQ_source.Digital_Output.name)) + source=DAQ_NIDAQ_source.Digital_Output)) channels = [ch for ch in channels if self.settings.child("devices").value() in ch.name] return channels diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py index f660ac7..1cebbdf 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py @@ -105,32 +105,32 @@ def ini_detector(self, controller=None): self.commit_settings(self.settings.child('NIDAQ_type')) for ch in self.config_channels: try: - ch.analog_type = ch.analog_type.upper() - ch.termination = ch.termination.upper() + ch.analog_type = ch.analog_type + ch.termination = ch.termination except: pass - if self.settings.child("dev_to_use").value() in ch.name: + if self.settings.child("devices").value() in ch.name: self.settings.child('ai_channels').addNew(ch.name) param = [a for a in self.settings.child('ai_channels').childs if a.opts['title'] == ch.name][0] - self.settings.child("ai_channels", param.opts['name'], "ai_type").setValue(ch.analog_type.upper()) - param.child("voltage_settings").show(ch.analog_type == UsageTypeAI.VOLTAGE.name) - param.child("current_settings").show(ch.analog_type == UsageTypeAI.CURRENT.name) - param.child("thermoc_settings").show(ch.analog_type == UsageTypeAI.TEMPERATURE_THERMOCOUPLE.name) - if ch.analog_type == UsageTypeAI.VOLTAGE.name: + self.settings.child("ai_channels", param.opts['name'], "ai_type").setValue(ch.analog_type.name) + param.child("voltage_settings").show(ch.analog_type == UsageTypeAI.VOLTAGE) + param.child("current_settings").show(ch.analog_type == UsageTypeAI.CURRENT) + param.child("thermoc_settings").show(ch.analog_type == UsageTypeAI.TEMPERATURE_THERMOCOUPLE) + if ch.analog_type == UsageTypeAI.VOLTAGE: self.settings.child("ai_channels", param.opts['name'], "voltage_settings", "volt_min").setValue( ch.value_min) self.settings.child("ai_channels", param.opts['name'], "voltage_settings", "volt_max").setValue( ch.value_max) self.settings.child("ai_channels", param.opts['name'], "termination").setValue( ch.termination.name) - elif ch.analog_type == UsageTypeAI.CURRENT.name: + elif ch.analog_type == UsageTypeAI.CURRENT: self.settings.child("ai_channels", param.opts['name'], "current_settings", "curr_min").setValue( ch.value_min) self.settings.child("ai_channels", param.opts['name'], "current_settings", "curr_max").setValue( ch.value_max) self.settings.child("ai_channels", param.opts['name'], "termination").setValue( ch.termination.name) - elif ch.analog_type == UsageTypeAI.TEMPERATURE_THERMOCOUPLE.name: + elif ch.analog_type == UsageTypeAI.TEMPERATURE_THERMOCOUPLE: self.settings.child("ai_channels", param.opts['name'], "thermoc_settings", @@ -144,7 +144,7 @@ def ini_detector(self, controller=None): "thermoc_settings", "T_max").setValue(ch.value_max) self.settings.child("ai_channels", param.opts['name'], "termination").setValue( - TerminalConfiguration.DEFAULT) + TerminalConfiguration.DEFAULT.name) info = "Plugin Initialized" initialized = True logger.info("Detector 0D initialized") diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index c64c8b6..59a15f8 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -1,13 +1,12 @@ import time import traceback -from enum import IntEnum +from enum import Enum import numpy as np from pymodaq.utils.logger import set_logger, get_module_name from nidaqmx.constants import AcquisitionType, VoltageUnits, CurrentUnits, CurrentShuntResistorLocation, \ TemperatureUnits, CJCSource, CountDirection, Level, FrequencyUnits, TimeUnits, \ - LineGrouping -from pymodaq_plugins_daqmx.hardware.national_instruments import UsageTypeAI, Edge, TerminalConfiguration, ThermocoupleType + LineGrouping, UsageTypeAI, UsageTypeAO, Edge, TerminalConfiguration, ThermocoupleType from nidaqmx.system import System as niSystem from nidaqmx.system.device import Device as niDevice @@ -19,29 +18,7 @@ logger = set_logger(get_module_name(__file__)) -class IntEnumExtend(IntEnum): - """ - This class expose 3 privates and undocumented methods of Enum - """ - @classmethod - def names(cls): - return cls._member_names_ - # [item.name for item in cls.__members__.items()] - # [name for name, member in cls.__members__.items()] - - @classmethod - def members(cls): - return list(cls._member_map_.values()) - # [member for name, member in cls.__members__.items()] - # [item.member for item in cls.__members__.items()] - - @classmethod - def values(cls): - return [cls[name].value for name in cls._member_names_] - # [cls[name].value for name, member in cls.__members__.items()] - - -class DAQ_NIDAQ_source(IntEnumExtend): +class DAQ_NIDAQ_source(Enum): """ Enum class of NIDAQ_source @@ -112,7 +89,7 @@ def __init__(self, name='', source=DAQ_NIDAQ_source.Analog_Input): """ self.name = name - assert source in DAQ_NIDAQ_source.names() + assert source in DAQ_NIDAQ_source self.source = source @@ -127,6 +104,7 @@ def __init__(self, analog_type=UsageTypeAI.VOLTAGE, value_min=-10., value_max=+1 super().__init__(**kwargs) self.value_min = value_min self.value_max = value_max + assert analog_type in UsageTypeAI self.analog_type = analog_type @@ -151,8 +129,8 @@ def __init__(self, **kwargs): class Counter(Channel): def __init__(self, edge=Edge.RISING, **kwargs): - assert edge in Edge super().__init__(**kwargs) + assert edge in Edge self.edge = edge self.counter_type = "Edge Counter" @@ -250,7 +228,7 @@ def get_NIDAQ_channels(cls, devices=None, source_type=None): devices = cls.get_NIDAQ_devices().device_names if source_type is None: - source_type = DAQ_NIDAQ_source.names() + source_type = DAQ_NIDAQ_source if not isinstance(source_type, list): source_type = [source_type] channels_tot = [] @@ -258,17 +236,17 @@ def get_NIDAQ_channels(cls, devices=None, source_type=None): if not not devices: for device in devices: for source in source_type: - if source == DAQ_NIDAQ_source.Analog_Input.name: # analog input + if source == DAQ_NIDAQ_source.Analog_Input: # analog input channels = niDevice(device).ai_physical_chans.channel_names - elif source == DAQ_NIDAQ_source.Analog_Output.name: # analog output + elif source == DAQ_NIDAQ_source.Analog_Output: # analog output channels = niDevice(device).ao_physical_chans.channel_names - elif source == DAQ_NIDAQ_source.Counter.name: # counter + elif source == DAQ_NIDAQ_source.Counter: # counter channels = niDevice(device).ci_physical_chans.channel_names - elif source == DAQ_NIDAQ_source.Digital_Output.name: # digital output + elif source == DAQ_NIDAQ_source.Digital_Output: # digital output channels = niDevice(device).do_lines.channel_names - elif source == DAQ_NIDAQ_source.Digital_Input.name: # digital iutput + elif source == DAQ_NIDAQ_source.Digital_Input: # digital iutput channels = niDevice(device).di_lines.channel_names - elif source == DAQ_NIDAQ_source.Terminals.name: # terminals + elif source == DAQ_NIDAQ_source.Terminals: # terminals channels = niDevice(device).terminals if channels != ['']: @@ -313,42 +291,44 @@ def configuration_sequence(self, viewer, current_device): except AssertionError as err: logger.error("Module {} not detected: {}".format(module_name, err)) continue - for source in config["NIDAQ_Devices", dev, mod]: - if not isinstance(config["NIDAQ_Devices", dev, mod, source], dict): + for src in config["NIDAQ_Devices", dev, mod]: + if not isinstance(config["NIDAQ_Devices", dev, mod, src], dict): continue - if source == "ai": - ai = config["NIDAQ_Devices", dev, mod, source] + if src == "ai": + ai = config["NIDAQ_Devices", dev, mod, src] for ch in ai.keys(): name = module_name + "/" + str(ch) - term = ai[ch].get("termination") - if ai[ch].get("analog_type") == UsageTypeAI.VOLTAGE.name: + source = DAQ_NIDAQ_source[ai[ch].get("source")] + analog_type = UsageTypeAI[ai[ch].get("analog_type")] + if analog_type == UsageTypeAI.VOLTAGE: + term = TerminalConfiguration[ai[ch].get("termination")] viewer.config_channels.append(AIChannel (name=name, - source=ai[ch].get("source"), - analog_type=ai[ch].get("analog_type"), + source=source, + analog_type=analog_type, value_min=float(ai[ch].get("value_min")), value_max=float(ai[ch].get("value_max")), - termination=TerminalConfiguration.__getitem__(term), + termination=term, )) - elif ai[ch].get("analog_type") == UsageTypeAI.CURRENT.name: + elif analog_type == UsageTypeAI.CURRENT: + term = TerminalConfiguration[ai[ch].get("termination")] viewer.config_channels.append(AIChannel (name=name, - source=ai[ch].get("source"), - analog_type=ai[ch].get("analog_type"), + source=source, + analog_type=analog_type, value_min=float(ai[ch].get("value_min")), value_max=float(ai[ch].get("value_max")), - termination=TerminalConfiguration.__getitem__(term), + termination=term, )) - elif ai[ch].get("analog_type") == UsageTypeAI.TEMPERATURE_THERMOCOUPLE.name or \ - ai[ch].get("analog_type") == "Thermocouple": - th = ai[ch].get("thermo_type") + elif analog_type == UsageTypeAI.TEMPERATURE_THERMOCOUPLE: + th = ThermocoupleType[ai[ch].get("thermo_type")] viewer.config_channels.append(AIThermoChannel (name=name, - source=ai[ch].get("source"), - analog_type=ai[ch].get("analog_type"), + source=source, + analog_type=analog_type, value_min=float(ai[ch].get("value_min")), value_max=float(ai[ch].get("value_max")), - thermo_type=ThermocoupleType.__getitem__(th), + thermo_type=th, )) logger.info("Devices from config: {}".format(viewer.config_devices)) logger.info("Modules from config: {}".format(viewer.config_modules)) @@ -410,9 +390,9 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti # create all channels one task for one type of channels for channel in channels: - if channel.source == DAQ_NIDAQ_source.Analog_Input.name: # analog input + if channel.source == DAQ_NIDAQ_source.Analog_Input: # analog input try: - if channel.analog_type == UsageTypeAI.VOLTAGE.name: + if channel.analog_type == UsageTypeAI.VOLTAGE: self._task.ai_channels.add_ai_voltage_chan(channel.name, "", channel.termination, @@ -421,7 +401,7 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti VoltageUnits.VOLTS, "") - elif channel.analog_type == UsageTypeAI.CURRENT.name: + elif channel.analog_type == UsageTypeAI.CURRENT: self._task.ai_channels.add_ai_current_chan(channel.name, "", channel.termination, @@ -432,7 +412,7 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti 0., "") - elif channel.analog_type == UsageTypeAI.TEMPERATURE_THERMOCOUPLE.name: + elif channel.analog_type == UsageTypeAI.TEMPERATURE_THERMOCOUPLE: self._task.ai_channels.add_ai_thrmcpl_chan(channel.name, "", channel.value_min, @@ -478,15 +458,15 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti if not not err_code: status = self.DAQmxGetErrorString(err_code) raise IOError(status) - elif channel.source == DAQ_NIDAQ_source.Analog_Output.name: # Analog_Output + elif channel.source == DAQ_NIDAQ_source.Analog_Output: # Analog_Output try: - if channel.analog_type == UsageTypeAI.VOLTAGE.name: + if channel.analog_type == UsageTypeAI.VOLTAGE: self._task.ao_channels.add_ao_voltage_chan(channel.name, "", channel.value_min, channel.value_max, VoltageUnits.VOLTS, None) - elif channel.analog_type == UsageTypeAI.CURRENT.name: + elif channel.analog_type == UsageTypeAI.CURRENT: self._task.ao_channels.add_ao_current_chan(channel.name, "", channel.value_min, channel.value_max, @@ -496,7 +476,7 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti if not not err_code: status = self.DAQmxGetErrorString(err_code) raise IOError(status) - elif channel.source == DAQ_NIDAQ_source.Digital_Output.name: + elif channel.source == DAQ_NIDAQ_source.Digital_Output: try: self._task.do_channels.add_do_chan(channel.name, "", LineGrouping.CHAN_PER_LINE) @@ -505,7 +485,7 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti if not not err_code: status = self.DAQmxGetErrorString(err_code) raise IOError(status) - elif channel.source == DAQ_NIDAQ_source.Digital_Input.name: # Digital_Input + elif channel.source == DAQ_NIDAQ_source.Digital_Input: # Digital_Input try: self._task.di_channels.add_di_chan(channel.name, "", LineGrouping.CHAN_PER_LINE) From 67ef2f827ec9abc27ad4d9639713bc7f20c9c5f4 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Tue, 21 Jan 2025 16:13:13 +0100 Subject: [PATCH 45/57] unused or extra codes removed --- .../daq_move_plugins/daq_move_NIDAQmx.py | 0 .../plugins_0D/daq_0Dviewer_DAQmx_counter.py | 138 ------------------ .../counter_clock_nidaqmx.py | 42 ------ .../counter_simple_nidaqmx.py | 41 ------ 4 files changed, 221 deletions(-) delete mode 100644 src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_NIDAQmx.py delete mode 100644 src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py delete mode 100644 src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_clock_nidaqmx.py delete mode 100644 src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_simple_nidaqmx.py diff --git a/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_NIDAQmx.py b/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_NIDAQmx.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py deleted file mode 100644 index 7add9e6..0000000 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx_counter.py +++ /dev/null @@ -1,138 +0,0 @@ -import numpy as np -from pymodaq.utils.daq_utils import ThreadCommand -from pymodaq.utils.data import DataWithAxes, DataToExport, DataSource -from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters, main -from pymodaq.utils.parameter import Parameter -import nidaqmx - -from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import DAQmx, Edge, Counter - - -class DAQ_0DViewer_DAQmx_counter(DAQ_Viewer_base): - """ - Plugin for a 0D PL counter, based on a NI card. - """ - params = comon_parameters+[ - {"title": "Counting channel:", "name": "counter_channel", - "type": "list", "limits": DAQmx.get_NIDAQ_channels(source_type="Counter")}, - {"title": "Acq. Time (s):", "name": "acq_time", - "type": "float", "value": 1., "default": 1.}, - ] - - def ini_attributes(self): - self.controller = None - self.clock_channel = None - self.counter_channel = None - self.live = False # True during a continuous grab - self.counting_time = 0.1 - - def commit_settings(self, param: Parameter): - """Apply the consequences of a change of value in the detector settings - - Parameters - ---------- - param: Parameter - A given parameter (within detector_settings) whose value has been changed by the user - """ - if param.name() == "acq_time": - self.counting_time = param.value() - self.update_tasks() - - def ini_detector(self, controller=None): - """Detector communication initialization - - Parameters - ---------- - controller: (object) - custom object of a PyMoDAQ plugin (Slave case). None if only one actuator/detector by controller - (Master case) - - Returns - ------- - info: str - initialized: bool - False if initialization failed otherwise True - """ - self.controller = DAQmx() #{"clock": DAQmx(), "counter": DAQmx()} - try: - self.update_tasks() - initialized = True - info = "NI card based counter" - self.counting_time = self.settings.child("acq_time").value() - except Exception as e: - print(e) - initialized = False - info = "Error" - - return info, initialized - - def close(self): - """Terminate the communication protocol""" - pass - #self.controller["clock"].close() - #self.controller["counter"].close() - - def grab_data(self, Naverage=1, **kwargs): - """Start a grab from the detector - - Parameters - ---------- - Naverage: int - Number of hardware averaging not relevant here. - kwargs: dict - others optionals arguments - """ - #update = False # to decide if we do the initial set up or not - - - #if 'live' in kwargs: - # if kwargs['live'] != self.live: - # update = True - # self.live = kwargs['live'] - # """ - #if 'live' in kwargs: - # if kwargs['live'] == self.live: - # update = False # we are already live - # self.live = kwargs['live'] - # """ - - #if update: - # self.update_tasks() - # self.controller["clock"].start() - - read_data = 32#self.controller.readCounter()#, counting_time=self.counting_time) - data = read_data #/self.counting_time # convert to cts/s - self.dte_signal.emit(DataToExport(name='Counts', - data=[DataWithAxes(name='Counts', data=[np.array([data])], - source=DataSource['raw'], - dim='Data0D', labels=['Counts (Hz)'])])) - - def stop(self): - """Stop the current grab hardware wise if necessary""" - self.task.stop() - self.close() - self.emit_status(ThreadCommand('Update_Status', ['Acquisition stopped.'])) - return '' - - def update_tasks(self): - """Set up the counting tasks in the NI card.""" - # Create channels - - self.counter_channel = Counter(name=self.settings.child("counter_channel").value(), - source="Counter", edge=Edge.names()[0]) - # - #self.controller["clock"].update_task(channels=[self.clock_channel], - # clock_settings=ClockSettings(), - # trigger_settings=TriggerSettings()) - #self.controller["clock"].task.CfgImplicitTiming(DAQmx_Val_ContSamps, 1) - # - #self.controller["counter"].update_task(channels=[self.counter_channel], - # clock_settings=ClockSettings(), - # trigger_settings=TriggerSettings()) - - # connect the clock to the counter - #self.controller["counter"].task.SetSampClkSrc("/" + self.clock_channel.name + "InternalOutput") - - -if __name__ == '__main__': - main(__file__) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_clock_nidaqmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_clock_nidaqmx.py deleted file mode 100644 index 2f5577f..0000000 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_clock_nidaqmx.py +++ /dev/null @@ -1,42 +0,0 @@ -import nidaqmx -import numpy as np -from nidaqmx.constants import Edge, AcquisitionType, FillMode, LineGrouping, CountDirection -from nidaqmx.stream_readers import CounterReader -from ctypes import c_uint32 - -def main(): - error = 0 - task_handle = nidaqmx.Task() - data = np.zeros(0, dtype=np.uint32) - read = np.uint32() - - try: - # DAQmx Configure Code - task_handle.ci_channels.add_ci_count_edges_chan("Dev1/ctr0", edge=Edge.RISING, initial_count=0, count_direction=CountDirection.COUNT_UP) - task_handle.timing.cfg_samp_clk_timing(source="/Dev1/Ctr1InternalOutput", rate=1000.0, active_edge=Edge.RISING, sample_mode=AcquisitionType.CONTINUOUS, samps_per_chan=1000) - - counter_reader=CounterReader(task_handle.in_stream) - # DAQmx Start Code - task_handle.start() - - print("En cours de lecture en continu. Appuyez sur Ctrl+C pour interrompre\n") - while True: - # DAQmx Read Code - #data=task_handle.read(number_of_samples_per_channel=1000, timeout=10.0) - #task_handle.in_stream.read_all_avail_samp - read=counter_reader.read_many_sample_uint32(data) - print("\rAcquis {} échantillons".format(read), end='', flush=True) - - except Exception as e: - print("\n") - if task_handle.is_task_done() == 0: - task_handle.stop() - task_handle.close() - print("Erreur DAQmx : {}".format(str(e))) - - finally: - print("\nFin du programme. Appuyez sur la touche Entrée pour quitter\n") - input() - -if __name__ == "__main__": - main() diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_simple_nidaqmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_simple_nidaqmx.py deleted file mode 100644 index d6ceedc..0000000 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/counter_simple_nidaqmx.py +++ /dev/null @@ -1,41 +0,0 @@ -import nidaqmx -import numpy as np -import time - -# Constantes équivalentes aux macros en C -DAQmx_Val_Rising = nidaqmx.constants.Edge.RISING -DAQmx_Val_CountUp = nidaqmx.constants.CountDirection.COUNT_UP - -def main(): - error = 0 - task_handle = nidaqmx.Task() - data = np.zeros(1, dtype=np.uint32) - - try: - # DAQmx Configure Code - task_handle.ci_channels.add_ci_count_edges_chan("Dev1/ctr0", edge=DAQmx_Val_Rising, initial_count=0, count_direction=DAQmx_Val_CountUp) - - # DAQmx Start Code - task_handle.start() - - print("En cours de lecture en continu. Appuyez sur Ctrl+C pour interrompre\n") - while True: - # DAQmx Read Code - data=task_handle.read(number_of_samples_per_channel=1) - - print("\rCompteur : {}".format(data[0]), end='', flush=True) - time.sleep(0.1) # Intervalle pour éviter une utilisation excessive du processeur - - except Exception as e: - print("\n") - if task_handle.is_task_done() == 0: - task_handle.stop() - task_handle.close() - print("Erreur DAQmx : {}".format(str(e))) - - finally: - print("\nFin du programme. Appuyez sur la touche Entrée pour quitter\n") - input() - -if __name__ == "__main__": - main() From 6a03efc638fe4b5f9af9e43b303b7ca20e681950 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Tue, 21 Jan 2025 16:16:22 +0100 Subject: [PATCH 46/57] PyDAQmx based-on Analog Input codes deprecated (use daq_0Dviewer_NIDAQmx.py instead) --- .../plugins_0D/daq_0Dviewer_DAQmxAI.py | 168 --------------- .../plugins_0D/daq_0Dviewer_DAQmxDualAI.py | 201 ------------------ 2 files changed, 369 deletions(-) delete mode 100644 src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmxAI.py delete mode 100644 src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmxDualAI.py diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmxAI.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmxAI.py deleted file mode 100644 index 35383e2..0000000 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmxAI.py +++ /dev/null @@ -1,168 +0,0 @@ -import numpy as np -from easydict import EasyDict as edict -from pymodaq.utils.daq_utils import ThreadCommand, getLineInfo -from pymodaq.utils.data import DataFromPlugins, Axis -from pymodaq.utils.logger import set_logger, get_module_name -from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters, main - -from pymodaq_plugins_daqmx.hardware.national_instruments.daqmx import DAQmx, DAQ_analog_types, DAQ_thermocouples,\ - DAQ_termination, Edge, DAQ_NIDAQ_source, \ - ClockSettings, AIChannel, Counter, AIThermoChannel, AOChannel, TriggerSettings, DOChannel, DIChannel - -logger = set_logger(get_module_name(__file__)) - - -class DAQ_0DViewer_DAQmxAI(DAQ_Viewer_base): - """Specific DAQmx plugin for getting analog input data as 0D or 1D data - - """ - params = comon_parameters+[ - {'title': 'Display type:', 'name': 'display', 'type': 'list', 'limits': ['0D', '1D']}, - {'title': 'Frequency Acq.:', 'name': 'frequency', 'type': 'int', 'value': 1000, 'min': 1}, - {'title': 'Nsamples:', 'name': 'Nsamples', 'type': 'int', 'value': 100, 'default': 100, 'min': 1}, - {'title': 'AI:', 'name': 'ai_channel', 'type': 'list', - 'limits': DAQmx.get_NIDAQ_channels(source_type='Analog_Input'), - 'value': DAQmx.get_NIDAQ_channels(source_type='Analog_Input')[0]}, - ] - hardware_averaging = True - live_mode_available = True - - def __init__(self, parent=None, params_state=None): - super().__init__(parent, params_state) - - self.channels_ai = None - self.clock_settings = None - self.data_tot = None - self.live = False - self.Naverage = 1 - self.ind_average = 0 - - def commit_settings(self, param): - """ - """ - - self.update_tasks() - - def ini_detector(self, controller=None): - """Detector communication initialization - - Parameters - ---------- - controller: (object) custom object of a PyMoDAQ plugin (Slave case). None if only one detector by controller (Master case) - - Returns - ------- - self.status (edict): with initialization status: three fields: - * info (str) - * controller (object) initialized controller - *initialized: (bool): False if initialization failed otherwise True - """ - self.controller = self.ini_detector_init(controller, dict(ai=DAQmx())) - - self.update_tasks() - - info = "Current measurement ready" - initialized = True - - return info, initialized - - def update_tasks(self): - - self.channels_ai = [AIChannel(name=self.settings.child('ai_channel').value(), - source='Analog_Input', analog_type='Voltage', - value_min=-10., value_max=10., termination='Diff', ), - ] - - self.clock_settings_ai = ClockSettings(frequency=self.settings.child('frequency').value(), - Nsamples=self.settings.child('Nsamples').value(), repetition=self.live) - - self.controller['ai'].update_task(self.channels_ai, self.clock_settings_ai) - - def close(self): - """ - Terminate the communication protocol - """ - pass - ## - - def grab_data(self, Naverage=1, **kwargs): - """ - - Parameters - ---------- - Naverage: (int) Number of hardware averaging - kwargs: (dict) of others optionals arguments - """ - - - update = False - - if 'live' in kwargs: - if kwargs['live'] != self.live: - update = True - self.live = kwargs['live'] - - if Naverage != self.Naverage: - self.Naverage = Naverage - update = True - - if update: - self.update_tasks() - - self.ind_average = 0 - self.data_tot = np.zeros((len(self.channels_ai), self.clock_settings_ai.Nsamples)) - - while not self.controller['ai'].isTaskDone(): - self.stop() - if self.controller['ai'].c_callback is None: - self.controller['ai'].register_callback(self.read_data, 'Nsamples', self.clock_settings_ai.Nsamples) - self.controller['ai'].task.StartTask() - #self.read_data(None, None, None, None) - - def read_data(self, taskhandle, status, samples=0, callbackdata=None): - #print(f'going to read {self.clock_settings_ai.Nsamples} samples, callbakc {samples}') - data = self.controller['ai'].readAnalog(len(self.channels_ai), self.clock_settings_ai) - if not self.live: - self.stop() - self.ind_average += 1 - self.data_tot += 1 / self.Naverage * data - - if self.ind_average == self.Naverage: - self.emit_data(self.data_tot) - self.ind_average = 0 - self.data_tot = np.zeros((len(self.channels_ai), self.clock_settings_ai.Nsamples)) - - return 0 #mandatory for the PyDAQmx callback - - def emit_data(self, data): - channels_name = [ch.name for ch in self.channels_ai] - - if self.settings.child('display').value() == '0D': - data = np.mean(data, 1) - - #data = np.squeeze(data) - # print(f'shape is {data.shape}') - # print(data) - if len(self.channels_ai) == 1 and data.size == 1: - data_export = [np.array([data[0]])] - else: - data_export = [np.squeeze(data[ind, :]) for ind in range(len(self.channels_ai))] - - # print(f'data len is {len(data_export)} and shape is {data_export[0].shape}') - self.data_grabed_signal.emit([DataFromPlugins( - name='NI AI', - data=data_export, - dim=f'Data{self.settings.child("display").value()}', labels=channels_name)]) - - def stop(self): - try: - self.controller['ai'].task.StopTask() - except: - pass - ############################## - - return '' - - -if __name__ == '__main__': - main(__file__) diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmxDualAI.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmxDualAI.py deleted file mode 100644 index d66792c..0000000 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmxDualAI.py +++ /dev/null @@ -1,201 +0,0 @@ -import numpy as np -from qtpy.QtCore import QThread -from easydict import EasyDict as edict -from pymodaq.utils.daq_utils import ThreadCommand, getLineInfo -from pymodaq.utils.data import DataFromPlugins, Axis -from pymodaq.utils.logger import set_logger, get_module_name -from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters, main - -from pymodaq_plugins_daqmx.hardware.national_instruments.daqmx import DAQmx, DAQ_analog_types, DAQ_thermocouples,\ - DAQ_termination, Edge, DAQ_NIDAQ_source, \ - ClockSettings, AIChannel, Counter, AIThermoChannel, AOChannel, TriggerSettings, DOChannel, DIChannel - -logger = set_logger(get_module_name(__file__)) - - -class DAQ_0DViewer_DAQmxDualAI(DAQ_Viewer_base): - """ - """ - params = comon_parameters+[ - {'title': 'Display type:', 'name': 'display', 'type': 'list', 'limits': ['0D', '1D']}, - {'title': 'Frequency Acq.:', 'name': 'frequency', 'type': 'int', 'value': 1000, 'min': 1}, - {'title': 'Nsamples:', 'name': 'Nsamples', 'type': 'int', 'value': 100, 'default': 100, 'min': 1}, - {'title': 'AI0:', 'name': 'ai_channel0', 'type': 'list', - 'limits': DAQmx.get_NIDAQ_channels(source_type='Analog_Input'), - 'value': DAQmx.get_NIDAQ_channels(source_type='Analog_Input')[2]}, - {'title': 'AI1:', 'name': 'ai_channel1', 'type': 'list', - 'limits': DAQmx.get_NIDAQ_channels(source_type='Analog_Input'), - 'value': DAQmx.get_NIDAQ_channels(source_type='Analog_Input')[3]}, - ] - hardware_averaging = True - live_mode_available = True - - def __init__(self, parent=None, params_state=None): - super().__init__(parent, params_state) - - self.channels_ai = None - self.clock_settings = None - self.data_tot = None - self.live = False - self.Naverage = 1 - self.ind_average = 0 - - def commit_settings(self, param): - """ - """ - - self.update_tasks() - - def ini_detector(self, controller=None): - """Detector communication initialization - - Parameters - ---------- - controller: (object) custom object of a PyMoDAQ plugin (Slave case). None if only one detector by controller (Master case) - - Returns - ------- - self.status (edict): with initialization status: three fields: - * info (str) - * controller (object) initialized controller - *initialized: (bool): False if initialization failed otherwise True - """ - - try: - self.status.update(edict(initialized=False,info="", x_axis=None,y_axis=None,controller=None)) - if self.settings.child(('controller_status')).value() == "Slave": - if controller is None: - raise Exception('no controller has been defined externally while this detector is a slave one') - else: - self.controller = controller - else: - - self.controller = dict(ai=DAQmx()) - ##################################### - - self.update_tasks() - - - self.status.info = "Current measurement ready" - self.status.initialized = True - self.status.controller = self.controller - return self.status - - except Exception as e: - self.emit_status(ThreadCommand('Update_Status', [getLineInfo() + str(e), 'log'])) - self.status.info = getLineInfo() + str(e) - self.status.initialized = False - return self.status - - def update_tasks(self): - - self.channels_ai = [AIChannel(name=self.settings.child('ai_channel0').value(), - source='Analog_Input', analog_type='Voltage', - value_min=-10., value_max=10., termination='Diff', ), - AIChannel(name=self.settings.child('ai_channel1').value(), - source='Analog_Input', analog_type='Voltage', - value_min=-10., value_max=10., termination='Diff', ), - ] - - self.clock_settings_ai = ClockSettings(frequency=self.settings.child('frequency').value(), - Nsamples=self.settings.child('Nsamples').value(), repetition=self.live) - - self.controller['ai'].update_task(self.channels_ai, self.clock_settings_ai) - - - - def close(self): - """ - Terminate the communication protocol - """ - pass - ## - - def grab_data(self, Naverage=1, **kwargs): - """ - - Parameters - ---------- - Naverage: (int) Number of hardware averaging - kwargs: (dict) of others optionals arguments - """ - - - update = False - - if 'live' in kwargs: - if kwargs['live'] != self.live: - update = True - self.live = kwargs['live'] - - if Naverage != self.Naverage: - self.Naverage = Naverage - update = True - - if update: - self.update_tasks() - - self.ind_average = 0 - self.data_tot = np.zeros((len(self.channels_ai), self.clock_settings_ai.Nsamples)) - - while not self.controller['ai'].isTaskDone(): - self.stop() - if self.controller['ai'].c_callback is None: - self.controller['ai'].register_callback(self.read_data, 'Nsamples', self.clock_settings_ai.Nsamples) - self.controller['ai'].task.StartTask() - #QThread.msleep(500) - #self.read_data(None, 0) - - def read_data(self, taskhandle, status, samples=0, callbackdata=None): - #print(f'going to read {self.clock_settings_ai.Nsamples} samples, callbakc {samples}') - data = self.controller['ai'].readAnalog(len(self.channels_ai), self.clock_settings_ai) - if not self.live: - self.stop() - self.ind_average += 1 - - self.data_tot += 1 / self.Naverage * data.reshape(len(self.channels_ai), self.clock_settings_ai.Nsamples) - - if self.ind_average == self.Naverage: - self.emit_data(self.data_tot) - self.ind_average = 0 - self.data_tot = np.zeros((len(self.channels_ai), self.clock_settings_ai.Nsamples)) - - return 0 #mandatory for the PyDAQmx callback - - def emit_data(self, data): - channels_name = [ch.name for ch in self.channels_ai] - - if self.settings.child('display').value() == '0D': - data = np.mean(data, 1) - - #data = np.squeeze(data) - # print(f'shape is {data.shape}') - # print(data) - if len(self.channels_ai) == 1 and data.size == 1: - data_export = [np.array([data[0]])] - else: - data_export = [np.squeeze(data[ind]) for ind in range(len(self.channels_ai))] - - if self.settings.child('display').value() == '0D': - datatosend = [np.array([data_export[0]]), np.array([data_export[1]])] - else: - datatosend = [d for d in data_export] - - # print(f'data len is {len(data_export)} and shape is {data_export[0].shape}') - self.data_grabed_signal.emit([DataFromPlugins( - name='NI AI', - data=datatosend, - dim=f'Data{self.settings.child("display").value()}', labels=channels_name)]) - - def stop(self): - try: - self.controller['ai'].task.StopTask() - except: - pass - ############################## - - return '' - - -if __name__ == '__main__': - main(__file__) From df14254fdf389fc298a8df2b95f272373bf40333 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Tue, 21 Jan 2025 16:16:22 +0100 Subject: [PATCH 47/57] PyDAQmx based-on Analog Input codes deprecated (use daq_0Dviewer_NIDAQmx.py instead) --- .../plugins_0D/daq_0Dviewer_DAQmx.py | 26 --- .../plugins_0D/daq_0Dviewer_DAQmxAI.py | 168 --------------- .../plugins_0D/daq_0Dviewer_DAQmxDualAI.py | 201 ------------------ 3 files changed, 395 deletions(-) delete mode 100644 src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx.py delete mode 100644 src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmxAI.py delete mode 100644 src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmxDualAI.py diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx.py deleted file mode 100644 index e8dba9f..0000000 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmx.py +++ /dev/null @@ -1,26 +0,0 @@ -from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_Viewer -from pymodaq.control_modules.viewer_utility_classes import main - - - -class DAQ_0DViewer_DAQmx(DAQ_NIDAQmx_Viewer): - """ - ==================== ======================== - **Attributes** **Type** - *data_grabed_signal* instance of Signal - *params* dictionnary list - *task* - ==================== ======================== - - See Also - -------- - refresh_hardware - """ - control_type = "0D" # could be "0D", "1D" - - def __init__(self, *args, **kwargs): - super().__init__(*args, control_type=self.control_type, **kwargs) - - -if __name__ == '__main__': - main(__file__, False) diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmxAI.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmxAI.py deleted file mode 100644 index 35383e2..0000000 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmxAI.py +++ /dev/null @@ -1,168 +0,0 @@ -import numpy as np -from easydict import EasyDict as edict -from pymodaq.utils.daq_utils import ThreadCommand, getLineInfo -from pymodaq.utils.data import DataFromPlugins, Axis -from pymodaq.utils.logger import set_logger, get_module_name -from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters, main - -from pymodaq_plugins_daqmx.hardware.national_instruments.daqmx import DAQmx, DAQ_analog_types, DAQ_thermocouples,\ - DAQ_termination, Edge, DAQ_NIDAQ_source, \ - ClockSettings, AIChannel, Counter, AIThermoChannel, AOChannel, TriggerSettings, DOChannel, DIChannel - -logger = set_logger(get_module_name(__file__)) - - -class DAQ_0DViewer_DAQmxAI(DAQ_Viewer_base): - """Specific DAQmx plugin for getting analog input data as 0D or 1D data - - """ - params = comon_parameters+[ - {'title': 'Display type:', 'name': 'display', 'type': 'list', 'limits': ['0D', '1D']}, - {'title': 'Frequency Acq.:', 'name': 'frequency', 'type': 'int', 'value': 1000, 'min': 1}, - {'title': 'Nsamples:', 'name': 'Nsamples', 'type': 'int', 'value': 100, 'default': 100, 'min': 1}, - {'title': 'AI:', 'name': 'ai_channel', 'type': 'list', - 'limits': DAQmx.get_NIDAQ_channels(source_type='Analog_Input'), - 'value': DAQmx.get_NIDAQ_channels(source_type='Analog_Input')[0]}, - ] - hardware_averaging = True - live_mode_available = True - - def __init__(self, parent=None, params_state=None): - super().__init__(parent, params_state) - - self.channels_ai = None - self.clock_settings = None - self.data_tot = None - self.live = False - self.Naverage = 1 - self.ind_average = 0 - - def commit_settings(self, param): - """ - """ - - self.update_tasks() - - def ini_detector(self, controller=None): - """Detector communication initialization - - Parameters - ---------- - controller: (object) custom object of a PyMoDAQ plugin (Slave case). None if only one detector by controller (Master case) - - Returns - ------- - self.status (edict): with initialization status: three fields: - * info (str) - * controller (object) initialized controller - *initialized: (bool): False if initialization failed otherwise True - """ - self.controller = self.ini_detector_init(controller, dict(ai=DAQmx())) - - self.update_tasks() - - info = "Current measurement ready" - initialized = True - - return info, initialized - - def update_tasks(self): - - self.channels_ai = [AIChannel(name=self.settings.child('ai_channel').value(), - source='Analog_Input', analog_type='Voltage', - value_min=-10., value_max=10., termination='Diff', ), - ] - - self.clock_settings_ai = ClockSettings(frequency=self.settings.child('frequency').value(), - Nsamples=self.settings.child('Nsamples').value(), repetition=self.live) - - self.controller['ai'].update_task(self.channels_ai, self.clock_settings_ai) - - def close(self): - """ - Terminate the communication protocol - """ - pass - ## - - def grab_data(self, Naverage=1, **kwargs): - """ - - Parameters - ---------- - Naverage: (int) Number of hardware averaging - kwargs: (dict) of others optionals arguments - """ - - - update = False - - if 'live' in kwargs: - if kwargs['live'] != self.live: - update = True - self.live = kwargs['live'] - - if Naverage != self.Naverage: - self.Naverage = Naverage - update = True - - if update: - self.update_tasks() - - self.ind_average = 0 - self.data_tot = np.zeros((len(self.channels_ai), self.clock_settings_ai.Nsamples)) - - while not self.controller['ai'].isTaskDone(): - self.stop() - if self.controller['ai'].c_callback is None: - self.controller['ai'].register_callback(self.read_data, 'Nsamples', self.clock_settings_ai.Nsamples) - self.controller['ai'].task.StartTask() - #self.read_data(None, None, None, None) - - def read_data(self, taskhandle, status, samples=0, callbackdata=None): - #print(f'going to read {self.clock_settings_ai.Nsamples} samples, callbakc {samples}') - data = self.controller['ai'].readAnalog(len(self.channels_ai), self.clock_settings_ai) - if not self.live: - self.stop() - self.ind_average += 1 - self.data_tot += 1 / self.Naverage * data - - if self.ind_average == self.Naverage: - self.emit_data(self.data_tot) - self.ind_average = 0 - self.data_tot = np.zeros((len(self.channels_ai), self.clock_settings_ai.Nsamples)) - - return 0 #mandatory for the PyDAQmx callback - - def emit_data(self, data): - channels_name = [ch.name for ch in self.channels_ai] - - if self.settings.child('display').value() == '0D': - data = np.mean(data, 1) - - #data = np.squeeze(data) - # print(f'shape is {data.shape}') - # print(data) - if len(self.channels_ai) == 1 and data.size == 1: - data_export = [np.array([data[0]])] - else: - data_export = [np.squeeze(data[ind, :]) for ind in range(len(self.channels_ai))] - - # print(f'data len is {len(data_export)} and shape is {data_export[0].shape}') - self.data_grabed_signal.emit([DataFromPlugins( - name='NI AI', - data=data_export, - dim=f'Data{self.settings.child("display").value()}', labels=channels_name)]) - - def stop(self): - try: - self.controller['ai'].task.StopTask() - except: - pass - ############################## - - return '' - - -if __name__ == '__main__': - main(__file__) diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmxDualAI.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmxDualAI.py deleted file mode 100644 index d66792c..0000000 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_DAQmxDualAI.py +++ /dev/null @@ -1,201 +0,0 @@ -import numpy as np -from qtpy.QtCore import QThread -from easydict import EasyDict as edict -from pymodaq.utils.daq_utils import ThreadCommand, getLineInfo -from pymodaq.utils.data import DataFromPlugins, Axis -from pymodaq.utils.logger import set_logger, get_module_name -from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters, main - -from pymodaq_plugins_daqmx.hardware.national_instruments.daqmx import DAQmx, DAQ_analog_types, DAQ_thermocouples,\ - DAQ_termination, Edge, DAQ_NIDAQ_source, \ - ClockSettings, AIChannel, Counter, AIThermoChannel, AOChannel, TriggerSettings, DOChannel, DIChannel - -logger = set_logger(get_module_name(__file__)) - - -class DAQ_0DViewer_DAQmxDualAI(DAQ_Viewer_base): - """ - """ - params = comon_parameters+[ - {'title': 'Display type:', 'name': 'display', 'type': 'list', 'limits': ['0D', '1D']}, - {'title': 'Frequency Acq.:', 'name': 'frequency', 'type': 'int', 'value': 1000, 'min': 1}, - {'title': 'Nsamples:', 'name': 'Nsamples', 'type': 'int', 'value': 100, 'default': 100, 'min': 1}, - {'title': 'AI0:', 'name': 'ai_channel0', 'type': 'list', - 'limits': DAQmx.get_NIDAQ_channels(source_type='Analog_Input'), - 'value': DAQmx.get_NIDAQ_channels(source_type='Analog_Input')[2]}, - {'title': 'AI1:', 'name': 'ai_channel1', 'type': 'list', - 'limits': DAQmx.get_NIDAQ_channels(source_type='Analog_Input'), - 'value': DAQmx.get_NIDAQ_channels(source_type='Analog_Input')[3]}, - ] - hardware_averaging = True - live_mode_available = True - - def __init__(self, parent=None, params_state=None): - super().__init__(parent, params_state) - - self.channels_ai = None - self.clock_settings = None - self.data_tot = None - self.live = False - self.Naverage = 1 - self.ind_average = 0 - - def commit_settings(self, param): - """ - """ - - self.update_tasks() - - def ini_detector(self, controller=None): - """Detector communication initialization - - Parameters - ---------- - controller: (object) custom object of a PyMoDAQ plugin (Slave case). None if only one detector by controller (Master case) - - Returns - ------- - self.status (edict): with initialization status: three fields: - * info (str) - * controller (object) initialized controller - *initialized: (bool): False if initialization failed otherwise True - """ - - try: - self.status.update(edict(initialized=False,info="", x_axis=None,y_axis=None,controller=None)) - if self.settings.child(('controller_status')).value() == "Slave": - if controller is None: - raise Exception('no controller has been defined externally while this detector is a slave one') - else: - self.controller = controller - else: - - self.controller = dict(ai=DAQmx()) - ##################################### - - self.update_tasks() - - - self.status.info = "Current measurement ready" - self.status.initialized = True - self.status.controller = self.controller - return self.status - - except Exception as e: - self.emit_status(ThreadCommand('Update_Status', [getLineInfo() + str(e), 'log'])) - self.status.info = getLineInfo() + str(e) - self.status.initialized = False - return self.status - - def update_tasks(self): - - self.channels_ai = [AIChannel(name=self.settings.child('ai_channel0').value(), - source='Analog_Input', analog_type='Voltage', - value_min=-10., value_max=10., termination='Diff', ), - AIChannel(name=self.settings.child('ai_channel1').value(), - source='Analog_Input', analog_type='Voltage', - value_min=-10., value_max=10., termination='Diff', ), - ] - - self.clock_settings_ai = ClockSettings(frequency=self.settings.child('frequency').value(), - Nsamples=self.settings.child('Nsamples').value(), repetition=self.live) - - self.controller['ai'].update_task(self.channels_ai, self.clock_settings_ai) - - - - def close(self): - """ - Terminate the communication protocol - """ - pass - ## - - def grab_data(self, Naverage=1, **kwargs): - """ - - Parameters - ---------- - Naverage: (int) Number of hardware averaging - kwargs: (dict) of others optionals arguments - """ - - - update = False - - if 'live' in kwargs: - if kwargs['live'] != self.live: - update = True - self.live = kwargs['live'] - - if Naverage != self.Naverage: - self.Naverage = Naverage - update = True - - if update: - self.update_tasks() - - self.ind_average = 0 - self.data_tot = np.zeros((len(self.channels_ai), self.clock_settings_ai.Nsamples)) - - while not self.controller['ai'].isTaskDone(): - self.stop() - if self.controller['ai'].c_callback is None: - self.controller['ai'].register_callback(self.read_data, 'Nsamples', self.clock_settings_ai.Nsamples) - self.controller['ai'].task.StartTask() - #QThread.msleep(500) - #self.read_data(None, 0) - - def read_data(self, taskhandle, status, samples=0, callbackdata=None): - #print(f'going to read {self.clock_settings_ai.Nsamples} samples, callbakc {samples}') - data = self.controller['ai'].readAnalog(len(self.channels_ai), self.clock_settings_ai) - if not self.live: - self.stop() - self.ind_average += 1 - - self.data_tot += 1 / self.Naverage * data.reshape(len(self.channels_ai), self.clock_settings_ai.Nsamples) - - if self.ind_average == self.Naverage: - self.emit_data(self.data_tot) - self.ind_average = 0 - self.data_tot = np.zeros((len(self.channels_ai), self.clock_settings_ai.Nsamples)) - - return 0 #mandatory for the PyDAQmx callback - - def emit_data(self, data): - channels_name = [ch.name for ch in self.channels_ai] - - if self.settings.child('display').value() == '0D': - data = np.mean(data, 1) - - #data = np.squeeze(data) - # print(f'shape is {data.shape}') - # print(data) - if len(self.channels_ai) == 1 and data.size == 1: - data_export = [np.array([data[0]])] - else: - data_export = [np.squeeze(data[ind]) for ind in range(len(self.channels_ai))] - - if self.settings.child('display').value() == '0D': - datatosend = [np.array([data_export[0]]), np.array([data_export[1]])] - else: - datatosend = [d for d in data_export] - - # print(f'data len is {len(data_export)} and shape is {data_export[0].shape}') - self.data_grabed_signal.emit([DataFromPlugins( - name='NI AI', - data=datatosend, - dim=f'Data{self.settings.child("display").value()}', labels=channels_name)]) - - def stop(self): - try: - self.controller['ai'].task.StopTask() - except: - pass - ############################## - - return '' - - -if __name__ == '__main__': - main(__file__) From 90941284350db028a3498db63d96af83635a2d92 Mon Sep 17 00:00:00 2001 From: Aurore Finco Date: Tue, 21 Jan 2025 18:01:30 +0100 Subject: [PATCH 48/57] Typos in daq_NIDAQmx_Move.py --- .../hardware/national_instruments/daq_NIDAQmx_Move.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Move.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Move.py index d38ca0b..396e910 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Move.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Move.py @@ -154,7 +154,7 @@ def move_abs(self, position): Parameters ---------- - position: (flaot) value of the absolute target positioning + position: (float) value of the absolute target positioning """ position = self.check_bound(position) # if user checked bounds, the defined bounds are applied here @@ -191,7 +191,7 @@ def move_rel(self, position): Parameters ---------- - position: (flaot) value of the relative target positioning + position: (float) value of the relative target positioning """ position = self.check_bound(self.current_position + position) - self.current_position From f721cfd53ce989bd502d3fbad58ee23a95670574 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Fri, 24 Jan 2025 15:44:29 +0100 Subject: [PATCH 49/57] DAQ_NIDAQ_source enum replaced by nidaqmx ChannelType enum --- .../national_instruments/daq_NIDAQmx.py | 82 +++++++++++------ .../daq_NIDAQmx_Viewer.py | 16 ++-- .../hardware/national_instruments/daqmxni.py | 91 ++++++++----------- 3 files changed, 95 insertions(+), 94 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index d49d032..0b8876a 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -3,9 +3,9 @@ from pymodaq.utils.logger import set_logger, get_module_name from pymodaq.utils.parameter import Parameter from pymodaq.utils.parameter.pymodaq_ptypes import registerParameterType, GroupParameter -from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import DAQmx, Edge, DAQ_NIDAQ_source, ClockSettings, \ - AIChannel, Counter, AIThermoChannel, AOChannel, TriggerSettings, DOChannel, DIChannel, UsageTypeAI, UsageTypeAO, \ - ThermocoupleType, TerminalConfiguration +from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import DAQmx, Edge, ChannelType, ClockSettings, \ + AIChannel, AIThermoChannel, AOChannel, CIChannel, COChannel, DOChannel, DIChannel, UsageTypeAI, UsageTypeAO, \ + ThermocoupleType, TerminalConfiguration, TriggerSettings logger = set_logger(get_module_name(__file__)) @@ -254,7 +254,7 @@ class DAQ_NIDAQmx_base: param_instance = DAQmx() params = [{'title': 'Refresh hardware:', 'name': 'refresh_hardware', 'type': 'bool', 'value': False}, {'title': 'Signal type:', 'name': 'NIDAQ_type', 'type': 'list', - 'limits': [Ds.name for Ds in DAQ_NIDAQ_source]}, + 'limits': [Ds.name for Ds in ChannelType]}, {'title': 'NSamples To Read', 'name': 'nsamplestoread', 'type': 'int', 'value': 1000, 'default': 1000, 'min': 1}, {'title': 'AO Settings:', 'name': 'ao_settings', 'type': 'group', 'children': [ @@ -277,18 +277,20 @@ class DAQ_NIDAQmx_base: ] }, {'title': 'AI Channels:', 'name': 'ai_channels', 'type': 'groupai', - 'limits': DAQmx.get_NIDAQ_channels(source_type=DAQ_NIDAQ_source.Analog_Input)}, + 'limits': DAQmx.get_NIDAQ_channels(source_type=ChannelType.ANALOG_INPUT)}, {'title': 'AO Channels:', 'name': 'ao_channels', 'type': 'groupao', - 'limits': DAQmx.get_NIDAQ_channels(source_type=DAQ_NIDAQ_source.Analog_Output)}, + 'limits': DAQmx.get_NIDAQ_channels(source_type=ChannelType.ANALOG_OUTPUT)}, {'title': 'DO Channels:', 'name': 'do_channels', 'type': 'groupdo', - 'limits': DAQmx.get_NIDAQ_channels(source_type=DAQ_NIDAQ_source.Digital_Output)}, + 'limits': DAQmx.get_NIDAQ_channels(source_type=ChannelType.DIGITAL_OUTPUT)}, {'title': 'DI Channels:', 'name': 'di_channels', 'type': 'groupdi', - 'limits': DAQmx.get_NIDAQ_channels(source_type=DAQ_NIDAQ_source.Digital_Input)}, + 'limits': DAQmx.get_NIDAQ_channels(source_type=ChannelType.DIGITAL_INPUT)}, {'title': 'Counter Settings:', 'name': 'counter_settings', 'type': 'group', 'visible': True, 'children': [ {'title': 'Counting time (ms):', 'name': 'counting_time', 'type': 'float', 'value': 100., 'default': 100., 'min': 0.}, - {'title': 'Counting Channels:', 'name': 'counter_channels', 'type': 'groupcounter', - 'limits': DAQmx.get_NIDAQ_channels(source_type=DAQ_NIDAQ_source.Counter)}, + {'title': 'CI Channels:', 'name': 'ci_channels', 'type': 'groupcounter', + 'limits': DAQmx.get_NIDAQ_channels(source_type=ChannelType.COUNTER_INPUT)}, + {'title': 'CO Channels:', 'name': 'co_channels', 'type': 'groupcounter', + 'limits': DAQmx.get_NIDAQ_channels(source_type=ChannelType.COUNTER_OUTPUT)}, ]}, {'title': 'Trigger Settings:', 'name': 'trigger_settings', 'type': 'group', 'visible': True, 'children': [ {'title': 'Enable?:', 'name': 'enable', 'type': 'bool', 'value': False, }, @@ -327,7 +329,7 @@ def commit_settings(self, param: Parameter): if param.name() == 'NIDAQ_type': self.controller.update_NIDAQ_channels(param.value()) - if param.value() == DAQ_NIDAQ_source.Analog_Input.name: # analog input + if param.value() == ChannelType.ANALOG_INPUT.name: # analog input self.settings.child('clock_settings').show() self.settings.child('ai_channels').show() self.settings.child('ao_channels').hide() @@ -336,7 +338,7 @@ def commit_settings(self, param: Parameter): self.settings.child('do_channels').hide() self.settings.child('di_channels').hide() - elif param.value() == DAQ_NIDAQ_source.Analog_Output.name: # analog output + elif param.value() == ChannelType.ANALOG_OUTPUT.name: # analog output self.settings.child('clock_settings').show() self.settings.child('ai_channels').hide() self.settings.child('ao_channels').show() @@ -345,16 +347,25 @@ def commit_settings(self, param: Parameter): self.settings.child('do_channels').hide() self.settings.child('di_channels').hide() - elif param.value() == DAQ_NIDAQ_source.Counter.name: # counter input + elif param.value() == ChannelType.COUNTER_INPUT.name: # counter input self.settings.child('clock_settings').hide() self.settings.child('ai_channels').hide() self.settings.child('ao_channels').hide() self.settings.child('ao_settings').hide() - self.settings.child('counter_settings').show() + self.settings.child('counter_settings', 'ci_channels').show() self.settings.child('do_channels').hide() self.settings.child('di_channels').hide() - elif param.value() == DAQ_NIDAQ_source.Digital_Input.name: # Digital_Input + elif param.value() == ChannelType.COUNTER_OUTPUT.name: # counter output + self.settings.child('clock_settings').hide() + self.settings.child('ai_channels').hide() + self.settings.child('ao_channels').hide() + self.settings.child('ao_settings').hide() + self.settings.child('counter_settings', 'co_channels').show() + self.settings.child('do_channels').hide() + self.settings.child('di_channels').hide() + + elif param.value() == ChannelType.DIGITAL_INPUT.name: # Digital_Input self.settings.child('clock_settings').show() self.settings.child('ai_channels').hide() self.settings.child('ao_channels').show() @@ -363,7 +374,7 @@ def commit_settings(self, param: Parameter): self.settings.child('do_channels').hide() self.settings.child('di_channels').show() - elif param.value() == DAQ_NIDAQ_source.Digital_Output.name: # digital output + elif param.value() == ChannelType.DIGITAL_OUTPUT.name: # digital output self.settings.child('clock_settings').show() self.settings.child('ai_channels').hide() self.settings.child('ao_channels').hide() @@ -406,52 +417,63 @@ def update_task(self): def get_channels_from_settings(self): channels = [] - if self.settings['NIDAQ_type'] == DAQ_NIDAQ_source.Analog_Input.name: # analog input + if self.settings['NIDAQ_type'] == ChannelType.ANALOG_INPUT.name: # analog input + source = ChannelType.ANALOG_INPUT for channel in self.settings.child('ai_channels').children(): analog_type = UsageTypeAI[channel['ai_type']] if analog_type == UsageTypeAI.VOLTAGE: channels.append(AIChannel(name=channel.opts['title'], - source=DAQ_NIDAQ_source.Analog_Input, analog_type=analog_type, + source=source, analog_type=analog_type, value_min=channel['voltage_settings', 'volt_min'], value_max=channel['voltage_settings', 'volt_max'], termination=TerminalConfiguration[channel['termination']], )) elif analog_type == UsageTypeAI.CURRENT: channels.append(AIChannel(name=channel.opts['title'], - source=DAQ_NIDAQ_source.Analog_Input, analog_type=analog_type, + source=source, analog_type=analog_type, value_min=channel['current_settings', 'curr_min'], value_max=channel['current_settings', 'curr_max'], termination=TerminalConfiguration[channel['termination']], )) elif analog_type == UsageTypeAI.TEMPERATURE_THERMOCOUPLE: channels.append(AIThermoChannel(name=channel.opts['title'], - source=DAQ_NIDAQ_source.Analog_Input, analog_type=analog_type, + source=source, analog_type=analog_type, value_min=channel['thermoc_settings', 'T_min'], value_max=channel['thermoc_settings', 'T_max'], thermo_type=ThermocoupleType[ channel['thermoc_settings', 'thermoc_type']], )) - elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source.Analog_Output.name: # analog output + elif self.settings['NIDAQ_type'] == ChannelType.ANALOG_OUTPUT.name: # analog output + source = ChannelType.ANALOG_OUTPUT for channel in self.settings.child('ao_channels').children(): analog_type = UsageTypeAO[channel['ao_type']] channels.append(AOChannel(name=channel.opts['title'], - source=DAQ_NIDAQ_source.Analog_Output, analog_type=analog_type, + source=source, analog_type=analog_type, value_min=channel['voltage_settings', 'volt_min'], value_max=channel['voltage_settings', 'volt_max'], )) - elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source.Counter.name: # counter input - for channel in self.settings.child('counter_settings', 'counter_channels').children(): - channels.append(Counter(name=channel.opts['title'], - source=DAQ_NIDAQ_source.Counter, edge=Edge[channel['edge']])) + elif self.settings['NIDAQ_type'] == ChannelType.COUNTER_INPUT.name: # counter input + source = ChannelType.COUNTER_INPUT + for channel in self.settings.child('counter_settings', 'ci_channels').children(): + channels.append(CIChannel(name=channel.opts['title'], + source=source, edge=Edge[channel['edge']])) + + elif self.settings['NIDAQ_type'] == ChannelType.COUNTER_OUTPUT.name: # counter output + source = ChannelType.COUNTER_OUTPUT + for channel in self.settings.child('counter_settings', 'co_channels').children(): + channels.append(COChannel(name=channel.opts['title'], + source=source, edge=Edge[channel['edge']])) - elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source.Digital_Input.name: # digital input + elif self.settings['NIDAQ_type'] == ChannelType.DIGITAL_INPUT.name: # digital input + source = ChannelType.DIGITAL_INPUT for channel in self.settings.child('di_channels').children(): channels.append(DIChannel(name=channel.opts['title'], - source=DAQ_NIDAQ_source.Digital_Input)) + source=source)) - elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source.Digital_Output.name: # Digital output + elif self.settings['NIDAQ_type'] == ChannelType.DIGITAL_OUTPUT.name: # Digital output + source = ChannelType.DIGITAL_OUTPUT for channel in self.settings.child('do_channels').children(): channels.append(DOChannel(name=channel.opts['title'], - source=DAQ_NIDAQ_source.Digital_Output)) + source=source)) channels = [ch for ch in channels if self.settings.child("devices").value() in ch.name] return channels diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py index 1cebbdf..238051d 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py @@ -4,7 +4,7 @@ from qtpy import QtCore from .daqmxni import DAQmx from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_base, TerminalConfiguration, \ - UsageTypeAI, DAQ_NIDAQ_source + UsageTypeAI, ChannelType from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters as viewer_params from pymodaq.utils.daq_utils import ThreadCommand from pymodaq.utils.data import DataFromPlugins, DataToExport @@ -38,11 +38,13 @@ def __init__(self, parent=None, params_state=None, control_type="0D"): self.live = False self.control_type = control_type # could be "0D", "1D" or "Actuator" if self.control_type == "0D": - self.settings.child('NIDAQ_type').setLimits(DAQ_NIDAQ_source.sources0D()) # analog input and counter + self.settings.child('NIDAQ_type').setLimits([ChannelType.ANALOG_INPUT.name, + ChannelType.COUNTER_INPUT.name, + ChannelType.DIGITAL_INPUT.name]) # analog & digital input + counter elif self.control_type == "1D": - self.settings.child('NIDAQ_type').setLimits(DAQ_NIDAQ_source.sources1D()) + self.settings.child('NIDAQ_type').setLimits(ChannelType.ANALOG_INPUT.name) elif self.control_type == "Actuator": - self.settings.child('NIDAQ_type').setLimits(DAQ_NIDAQ_source.Actuator()) + self.settings.child('NIDAQ_type').setLimits(ChannelType.ANALOG_OUTPUT.name, ChannelType.COUNTER_OUTPUT.name) self.settings.child('ao_settings').hide() self.settings.child('ao_channels').hide() @@ -74,7 +76,7 @@ def commit_settings(self, param): See Also -------- - update_NIDAQ_channels, update_task, DAQ_NIDAQ_source, refresh_hardware + update_NIDAQ_channels, update_task, refresh_hardware """ if param.parent() is not None: @@ -167,10 +169,6 @@ def grab_data(self, Naverage=1, **kwargs): **Parameters** **Type** **Description** *Naverage* int Number of values to average =============== ======== =============================================== - - See Also - -------- - DAQ_NIDAQ_source """ update = False diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index 59a15f8..53ca05a 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -6,7 +6,8 @@ from nidaqmx.constants import AcquisitionType, VoltageUnits, CurrentUnits, CurrentShuntResistorLocation, \ TemperatureUnits, CJCSource, CountDirection, Level, FrequencyUnits, TimeUnits, \ - LineGrouping, UsageTypeAI, UsageTypeAO, Edge, TerminalConfiguration, ThermocoupleType + LineGrouping, UsageTypeAI, UsageTypeAO, UsageTypeCI, UsageTypeCO, Edge, \ + TerminalConfiguration, ThermocoupleType, ChannelType from nidaqmx.system import System as niSystem from nidaqmx.system.device import Device as niDevice @@ -18,36 +19,6 @@ logger = set_logger(get_module_name(__file__)) -class DAQ_NIDAQ_source(Enum): - """ - Enum class of NIDAQ_source - - =============== ========== - **Attributes** **Type** - *Analog_Input* int - *Counter* int - =============== ========== - """ - Analog_Input = 0 - Analog_Output = 1 - Counter = 2 - Digital_Input = 3 - Digital_Output = 4 - Terminals = 5 - - @classmethod - def sources0D(self): - return [self.Analog_Input.name, DAQ_NIDAQ_source.Counter.name, DAQ_NIDAQ_source.Digital_Input.name] - - @classmethod - def sources1D(self): - return [self.Analog_Input.name] - - @classmethod - def Actuator(self): - return [self.Analog_Output.name] - - class ClockSettingsBase: def __init__(self, Nsamples=1000, repetition=False): @@ -82,14 +53,14 @@ def __init__(self, trig_source='', enable=False, edge=Edge.RISING, level=0.1): class Channel: - def __init__(self, name='', source=DAQ_NIDAQ_source.Analog_Input): + def __init__(self, name='', source=ChannelType.ANALOG_INPUT): """ Parameters ---------- """ self.name = name - assert source in DAQ_NIDAQ_source + assert source in ChannelType self.source = source @@ -127,19 +98,30 @@ def __init__(self, **kwargs): super().__init__(**kwargs) -class Counter(Channel): - def __init__(self, edge=Edge.RISING, **kwargs): +class CIChannel(Channel): + def __init__(self, edge=Edge.RISING, counter_type=UsageTypeCI.COUNT_EDGES, **kwargs): super().__init__(**kwargs) assert edge in Edge + assert counter_type in UsageTypeCI self.edge = edge - self.counter_type = "Edge Counter" + self.counter_type = counter_type -class ClockCounter(Counter): - def __init__(self, clock_frequency=100, **kwargs): +class COChannel(Channel): + def __init__(self, edge=Edge.RISING, counter_type=UsageTypeCO.PULSE_FREQUENCY, **kwargs): + super().__init__(**kwargs) + assert edge in Edge + assert counter_type in UsageTypeCO + self.edge = edge + self.counter_type = counter_type + + +class ClockCounter(COChannel): + def __init__(self, clock_frequency=100, counter_type=UsageTypeCO.PULSE_FREQUENCY, **kwargs): + super().counter_type = counter_type super().__init__(**kwargs) self.clock_frequency = clock_frequency - self.counter_type = "Clock Output" + self.counter_type = counter_type class DigitalChannel(Channel): @@ -217,7 +199,7 @@ def get_NIDAQ_channels(cls, devices=None, source_type=None): devices: list list of strings, each one being a connected device source_type: str - One of the entries of DAQ_NIDAQ_source enum + Channels possible types Returns ------- @@ -228,7 +210,7 @@ def get_NIDAQ_channels(cls, devices=None, source_type=None): devices = cls.get_NIDAQ_devices().device_names if source_type is None: - source_type = DAQ_NIDAQ_source + source_type = ChannelType if not isinstance(source_type, list): source_type = [source_type] channels_tot = [] @@ -236,19 +218,18 @@ def get_NIDAQ_channels(cls, devices=None, source_type=None): if not not devices: for device in devices: for source in source_type: - if source == DAQ_NIDAQ_source.Analog_Input: # analog input + if source == ChannelType.ANALOG_INPUT: # analog input channels = niDevice(device).ai_physical_chans.channel_names - elif source == DAQ_NIDAQ_source.Analog_Output: # analog output + elif source == ChannelType.ANALOG_OUTPUT: # analog output channels = niDevice(device).ao_physical_chans.channel_names - elif source == DAQ_NIDAQ_source.Counter: # counter + elif source == ChannelType.COUNTER_INPUT: # counter input channels = niDevice(device).ci_physical_chans.channel_names - elif source == DAQ_NIDAQ_source.Digital_Output: # digital output - channels = niDevice(device).do_lines.channel_names - elif source == DAQ_NIDAQ_source.Digital_Input: # digital iutput + elif source == ChannelType.COUNTER_OUTPUT: # counter output + channels = niDevice(device).co_physical_chans.channel_names + elif source == ChannelType.DIGITAL_INPUT: # digital input channels = niDevice(device).di_lines.channel_names - elif source == DAQ_NIDAQ_source.Terminals: # terminals - channels = niDevice(device).terminals - + elif source == ChannelType.DIGITAL_OUTPUT: # digital output + channels = niDevice(device).do_lines.channel_names if channels != ['']: channels_tot.extend(channels) @@ -298,7 +279,7 @@ def configuration_sequence(self, viewer, current_device): ai = config["NIDAQ_Devices", dev, mod, src] for ch in ai.keys(): name = module_name + "/" + str(ch) - source = DAQ_NIDAQ_source[ai[ch].get("source")] + source = ChannelType[ai[ch].get("source")] analog_type = UsageTypeAI[ai[ch].get("analog_type")] if analog_type == UsageTypeAI.VOLTAGE: term = TerminalConfiguration[ai[ch].get("termination")] @@ -390,7 +371,7 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti # create all channels one task for one type of channels for channel in channels: - if channel.source == DAQ_NIDAQ_source.Analog_Input: # analog input + if channel.source == ChannelType.ANALOG_INPUT: # analog input try: if channel.analog_type == UsageTypeAI.VOLTAGE: self._task.ai_channels.add_ai_voltage_chan(channel.name, @@ -458,7 +439,7 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti if not not err_code: status = self.DAQmxGetErrorString(err_code) raise IOError(status) - elif channel.source == DAQ_NIDAQ_source.Analog_Output: # Analog_Output + elif channel.source == ChannelType.ANALOG_OUTPUT: # Analog_Output try: if channel.analog_type == UsageTypeAI.VOLTAGE: self._task.ao_channels.add_ao_voltage_chan(channel.name, "", @@ -476,7 +457,7 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti if not not err_code: status = self.DAQmxGetErrorString(err_code) raise IOError(status) - elif channel.source == DAQ_NIDAQ_source.Digital_Output: + elif channel.source == ChannelType.DIGITAL_OUTPUT: try: self._task.do_channels.add_do_chan(channel.name, "", LineGrouping.CHAN_PER_LINE) @@ -485,7 +466,7 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti if not not err_code: status = self.DAQmxGetErrorString(err_code) raise IOError(status) - elif channel.source == DAQ_NIDAQ_source.Digital_Input: # Digital_Input + elif channel.source == ChannelType.DIGITAL_INPUT: # Digital_Input try: self._task.di_channels.add_di_chan(channel.name, "", LineGrouping.CHAN_PER_LINE) From a2d9312ee6cde23192002b3b76c816bf4227672f Mon Sep 17 00:00:00 2001 From: Sebastien Date: Fri, 24 Jan 2025 15:45:37 +0100 Subject: [PATCH 50/57] README update --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 6839891..3187a00 100644 --- a/README.rst +++ b/README.rst @@ -41,6 +41,6 @@ Viewer0D ++++++++ * **DAQmx_PLcounter**: Single photon counting -* **NIDAQmx: Analog Input (current-voltage-temperature), working with cDAQ & DAQ-USB +* **NIDAQmx: For now (01/2025) Only Analog Input measurements (current-voltage-temperature), working with cDAQ & DAQ-USB From 92e97449bc365b6af2a4ecac598b53357979a2bd Mon Sep 17 00:00:00 2001 From: Sebastien Date: Mon, 27 Jan 2025 10:19:06 +0100 Subject: [PATCH 51/57] Renaming DAQmx object as NIDAQmx one --- .../plugins_0D/daq_0Dviewer_NIDAQmx.py | 6 +++--- .../national_instruments/daq_NIDAQmx.py | 18 +++++++++--------- .../national_instruments/daq_NIDAQmx_Viewer.py | 4 ++-- .../hardware/national_instruments/daqmxni.py | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py index 575c76b..df9ae89 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py @@ -1,7 +1,7 @@ from pymodaq.control_modules.viewer_utility_classes import main from pymodaq.control_modules.viewer_utility_classes import comon_parameters as viewer_params from pymodaq_plugins_daqmx import config -from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import AIChannel, AIThermoChannel, DAQmx, niTask, \ +from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import AIChannel, AIThermoChannel, NIDAQmx, niTask, \ niDevice, TemperatureUnits, CJCSource, VoltageUnits, AcquisitionType from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import UsageTypeAI, ThermocoupleType, \ TerminalConfiguration, Edge @@ -19,14 +19,14 @@ class DAQ_0DViewer_NIDAQmx(DAQ_NIDAQmx_Viewer): config_channels: list channels_ai: list config: config # todo review Useful/Unused - controller: DAQmx + controller: NIDAQmx config_devices: list config_modules: list current_device: niDevice live: bool Naverage: int - param_devices = DAQmx.get_NIDAQ_devices().device_names + param_devices = NIDAQmx.get_NIDAQ_devices().device_names params = viewer_params + [ {'title': 'Display type:', 'name': 'display', 'type': 'list', 'limits': ['0D', '1D']}, {'title': 'Devices :', 'name': 'devices', 'type': 'list', 'limits': param_devices, diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index 0b8876a..7218073 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -3,7 +3,7 @@ from pymodaq.utils.logger import set_logger, get_module_name from pymodaq.utils.parameter import Parameter from pymodaq.utils.parameter.pymodaq_ptypes import registerParameterType, GroupParameter -from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import DAQmx, Edge, ChannelType, ClockSettings, \ +from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import NIDAQmx, Edge, ChannelType, ClockSettings, \ AIChannel, AIThermoChannel, AOChannel, CIChannel, COChannel, DOChannel, DIChannel, UsageTypeAI, UsageTypeAO, \ ThermocoupleType, TerminalConfiguration, TriggerSettings @@ -251,7 +251,7 @@ class DAQ_NIDAQmx_base: Base NIDAQmx class for using DAQmx objects from daqmxni.py in the DAQ_NIDAQmx_Move & DAQ_NIDAQmx_Viewer """ data_grabed_signal = Signal(list) - param_instance = DAQmx() + param_instance = NIDAQmx() params = [{'title': 'Refresh hardware:', 'name': 'refresh_hardware', 'type': 'bool', 'value': False}, {'title': 'Signal type:', 'name': 'NIDAQ_type', 'type': 'list', 'limits': [Ds.name for Ds in ChannelType]}, @@ -277,25 +277,25 @@ class DAQ_NIDAQmx_base: ] }, {'title': 'AI Channels:', 'name': 'ai_channels', 'type': 'groupai', - 'limits': DAQmx.get_NIDAQ_channels(source_type=ChannelType.ANALOG_INPUT)}, + 'limits': NIDAQmx.get_NIDAQ_channels(source_type=ChannelType.ANALOG_INPUT)}, {'title': 'AO Channels:', 'name': 'ao_channels', 'type': 'groupao', - 'limits': DAQmx.get_NIDAQ_channels(source_type=ChannelType.ANALOG_OUTPUT)}, + 'limits': NIDAQmx.get_NIDAQ_channels(source_type=ChannelType.ANALOG_OUTPUT)}, {'title': 'DO Channels:', 'name': 'do_channels', 'type': 'groupdo', - 'limits': DAQmx.get_NIDAQ_channels(source_type=ChannelType.DIGITAL_OUTPUT)}, + 'limits': NIDAQmx.get_NIDAQ_channels(source_type=ChannelType.DIGITAL_OUTPUT)}, {'title': 'DI Channels:', 'name': 'di_channels', 'type': 'groupdi', - 'limits': DAQmx.get_NIDAQ_channels(source_type=ChannelType.DIGITAL_INPUT)}, + 'limits': NIDAQmx.get_NIDAQ_channels(source_type=ChannelType.DIGITAL_INPUT)}, {'title': 'Counter Settings:', 'name': 'counter_settings', 'type': 'group', 'visible': True, 'children': [ {'title': 'Counting time (ms):', 'name': 'counting_time', 'type': 'float', 'value': 100., 'default': 100., 'min': 0.}, {'title': 'CI Channels:', 'name': 'ci_channels', 'type': 'groupcounter', - 'limits': DAQmx.get_NIDAQ_channels(source_type=ChannelType.COUNTER_INPUT)}, + 'limits': NIDAQmx.get_NIDAQ_channels(source_type=ChannelType.COUNTER_INPUT)}, {'title': 'CO Channels:', 'name': 'co_channels', 'type': 'groupcounter', - 'limits': DAQmx.get_NIDAQ_channels(source_type=ChannelType.COUNTER_OUTPUT)}, + 'limits': NIDAQmx.get_NIDAQ_channels(source_type=ChannelType.COUNTER_OUTPUT)}, ]}, {'title': 'Trigger Settings:', 'name': 'trigger_settings', 'type': 'group', 'visible': True, 'children': [ {'title': 'Enable?:', 'name': 'enable', 'type': 'bool', 'value': False, }, {'title': 'Trigger Source:', 'name': 'trigger_channel', 'type': 'list', - 'limits': DAQmx.getTriggeringSources()}, + 'limits': NIDAQmx.getTriggeringSources()}, {'title': 'Edge type:', 'name': 'edge', 'type': 'list', 'limits': [e.name for e in Edge], 'visible': False}, {'title': 'Level:', 'name': 'level', 'type': 'float', 'value': 1., 'visible': False} diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py index 238051d..7bd33b2 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py @@ -2,7 +2,7 @@ import numpy as np import traceback from qtpy import QtCore -from .daqmxni import DAQmx +from .daqmxni import NIDAQmx from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_base, TerminalConfiguration, \ UsageTypeAI, ChannelType from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters as viewer_params @@ -100,7 +100,7 @@ def ini_detector(self, controller=None): """ try: self.current_device = nidaqmx.system.Device(self.settings["devices"]) - self.controller = self.ini_detector_init(controller, DAQmx()) + self.controller = self.ini_detector_init(controller, NIDAQmx()) self.controller.configuration_sequence(self, self.current_device) # actions to perform in order to set properly the settings tree options diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index 53ca05a..039fb6e 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -139,7 +139,7 @@ def __init__(self, **kwargs): super().__init__(**kwargs) -class DAQmx: +class NIDAQmx: """Wrapper around the NIDAQmx package giving an easy-to-use object to instantiate channels and tasks""" def __init__(self): self.devices = [] From a17455cb8d2246618abb6de65c99994492a49667 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Mon, 27 Jan 2025 10:28:29 +0100 Subject: [PATCH 52/57] README update --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 3187a00..b4f3222 100644 --- a/README.rst +++ b/README.rst @@ -41,6 +41,6 @@ Viewer0D ++++++++ * **DAQmx_PLcounter**: Single photon counting -* **NIDAQmx: For now (01/2025) Only Analog Input measurements (current-voltage-temperature), working with cDAQ & DAQ-USB +* **NIDAQmx**: For now (01/2025) Only Analog Input tested and working. (current-voltage-temperature measurements on cDAQ & DAQ-USB) From 34983c6edfdc0144a5c7fb12f72ea40fd17dd473 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Mon, 27 Jan 2025 10:44:56 +0100 Subject: [PATCH 53/57] Renaming new nidaqmx based-on codes (NIDAQmx_base, NIDAQmx_Viewer, NIDAQmx_Move) to let previous PYDAQmx based-on daq_NIDAQmx.py reachable --- .../daq_move_plugins/daq_move_DAQmx.py | 2 +- .../plugins_0D/daq_0Dviewer_NIDAQmx.py | 4 +- .../plugins_1D/daq_1Dviewer_DAQmx.py | 2 +- .../{daq_NIDAQmx_Move.py => NIDAQmx_Move.py} | 2 +- ...aq_NIDAQmx_Viewer.py => NIDAQmx_Viewer.py} | 2 +- .../national_instruments/NIDAQmx_base.py | 490 ++++++++++++ .../national_instruments/daq_NIDAQmx.py | 716 +++++++++++++----- 7 files changed, 1040 insertions(+), 178 deletions(-) rename src/pymodaq_plugins_daqmx/hardware/national_instruments/{daq_NIDAQmx_Move.py => NIDAQmx_Move.py} (99%) rename src/pymodaq_plugins_daqmx/hardware/national_instruments/{daq_NIDAQmx_Viewer.py => NIDAQmx_Viewer.py} (98%) create mode 100644 src/pymodaq_plugins_daqmx/hardware/national_instruments/NIDAQmx_base.py diff --git a/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_DAQmx.py b/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_DAQmx.py index f5e26db..f627b51 100644 --- a/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_DAQmx.py +++ b/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_DAQmx.py @@ -1,4 +1,4 @@ -from ..hardware.national_instruments.daq_NIDAQmx_Move import DAQ_NIDAQmx_Actuator +from ..hardware.national_instruments.NIDAQmx_Move import DAQ_NIDAQmx_Actuator class DAQ_Move_DAQmx(DAQ_NIDAQmx_Actuator): diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py index df9ae89..0c3895b 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_0D/daq_0Dviewer_NIDAQmx.py @@ -5,8 +5,8 @@ niDevice, TemperatureUnits, CJCSource, VoltageUnits, AcquisitionType from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import UsageTypeAI, ThermocoupleType, \ TerminalConfiguration, Edge -from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_base -from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx_Viewer import DAQ_NIDAQmx_Viewer +from pymodaq_plugins_daqmx.hardware.national_instruments.NIDAQmx_base import DAQ_NIDAQmx_base +from pymodaq_plugins_daqmx.hardware.national_instruments.NIDAQmx_Viewer import DAQ_NIDAQmx_Viewer from pymodaq.utils.logger import set_logger, get_module_name logger = set_logger(get_module_name(__file__)) diff --git a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_1D/daq_1Dviewer_DAQmx.py b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_1D/daq_1Dviewer_DAQmx.py index 6da3bf5..ebe032f 100644 --- a/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_1D/daq_1Dviewer_DAQmx.py +++ b/src/pymodaq_plugins_daqmx/daq_viewer_plugins/plugins_1D/daq_1Dviewer_DAQmx.py @@ -1,4 +1,4 @@ -from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx_Viewer import DAQ_NIDAQmx_Viewer +from pymodaq_plugins_daqmx.hardware.national_instruments.NIDAQmx_Viewer import DAQ_NIDAQmx_Viewer from pymodaq.control_modules.viewer_utility_classes import main diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Move.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/NIDAQmx_Move.py similarity index 99% rename from src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Move.py rename to src/pymodaq_plugins_daqmx/hardware/national_instruments/NIDAQmx_Move.py index d38ca0b..3ad598d 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Move.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/NIDAQmx_Move.py @@ -2,7 +2,7 @@ from easydict import EasyDict as edict from qtpy import QtWidgets from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, comon_parameters as actuator_params -from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_base +from pymodaq_plugins_daqmx.hardware.national_instruments.NIDAQmx_base import DAQ_NIDAQmx_base from pymodaq.utils.daq_utils import ThreadCommand, getLineInfo from pymodaq.utils.data import DataActuator from pymodaq.utils.logger import set_logger, get_module_name diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/NIDAQmx_Viewer.py similarity index 98% rename from src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py rename to src/pymodaq_plugins_daqmx/hardware/national_instruments/NIDAQmx_Viewer.py index 7bd33b2..ba15f27 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Viewer.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/NIDAQmx_Viewer.py @@ -3,7 +3,7 @@ import traceback from qtpy import QtCore from .daqmxni import NIDAQmx -from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_base, TerminalConfiguration, \ +from pymodaq_plugins_daqmx.hardware.national_instruments.NIDAQmx_base import DAQ_NIDAQmx_base, TerminalConfiguration, \ UsageTypeAI, ChannelType from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters as viewer_params from pymodaq.utils.daq_utils import ThreadCommand diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/NIDAQmx_base.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/NIDAQmx_base.py new file mode 100644 index 0000000..7218073 --- /dev/null +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/NIDAQmx_base.py @@ -0,0 +1,490 @@ +from qtpy import QtWidgets +from qtpy.QtCore import Signal +from pymodaq.utils.logger import set_logger, get_module_name +from pymodaq.utils.parameter import Parameter +from pymodaq.utils.parameter.pymodaq_ptypes import registerParameterType, GroupParameter +from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import NIDAQmx, Edge, ChannelType, ClockSettings, \ + AIChannel, AIThermoChannel, AOChannel, CIChannel, COChannel, DOChannel, DIChannel, UsageTypeAI, UsageTypeAO, \ + ThermocoupleType, TerminalConfiguration, TriggerSettings + + +logger = set_logger(get_module_name(__file__)) + + +class ScalableGroupAI(GroupParameter): + + """ + | + + ================ ============= + **Attributes** **Type** + *opts* dictionnary + ================ ============= + + See Also + -------- + hardware.DAQ_Move_Stage_type + """ + + params = [{'title': 'AI type:', 'name': 'ai_type', 'type': 'list', 'limits': [Uai.name for Uai in UsageTypeAI]}, + {'title': 'Voltage:', 'name': 'voltage_settings', 'type': 'group', 'children': [ + {'title': 'Voltage Min:', 'name': 'volt_min', 'type': 'float', 'value': -10.}, + {'title': 'Voltage Max:', 'name': 'volt_max', 'type': 'float', 'value': 10.}, + ]}, + {'title': 'Current:', 'name': 'current_settings', 'type': 'group', 'visible': False, 'children': [ + {'title': 'Current Min:', 'name': 'curr_min', 'type': 'float', 'value': -1, 'suffix': 'A'}, + {'title': 'Current Max:', 'name': 'curr_max', 'type': 'float', 'value': 1, 'suffix': 'A'}, + ]}, + {'title': 'Thermocouple:', 'name': 'thermoc_settings', 'type': 'group', 'visible': False, 'children': [ + {'title': 'Thc. type:', 'name': 'thermoc_type', 'type': 'list', + 'limits': [Th.name for Th in ThermocoupleType], 'value': 'K'}, + {'title': 'Temp. Min (°C):', 'name': 'T_min', 'type': 'float', 'value': 0, 'suffix': '°C'}, + {'title': 'Temp. Max (°C):', 'name': 'T_max', 'type': 'float', 'value': 50, 'suffix': '°C'}, + ]}, + {'title': 'Termination:', 'name': 'termination', 'type': 'list', + 'limits': [Te.name for Te in TerminalConfiguration]}, + ] + + def __init__(self, **opts): + opts['type'] = 'groupai' + opts['addText'] = "Add" + opts['addList'] = opts['limits'] + GroupParameter.__init__(self, **opts) + + def addNew(self, typ=None): + """ + Add a child. + + =============== =========== + **Parameters** **Type** + *typ* string + =============== =========== + """ + childnames = [par.name() for par in self.children()] + if childnames == []: + newindex = 0 + else: + newindex = len(childnames) + + child = {'title': typ, 'name': 'ai{:02.0f}'.format(newindex), 'type': 'group', 'children': self.params, + 'removable': True, 'renamable': False} + + self.addChild(child) + + +registerParameterType('groupai', ScalableGroupAI, override=True) + + +class ScalableGroupAO(GroupParameter): + """ + | + + ================ ============= + **Attributes** **Type** + *opts* dictionnary + ================ ============= + + See Also + -------- + hardware.DAQ_Move_Stage_type + """ + + params = [{'title': 'AO type:', 'name': 'ao_type', 'type': 'list', 'limits': [Uao.name for Uao in UsageTypeAO]}, + {'title': 'Voltages:', 'name': 'voltage_settings', 'type': 'group', 'children': [ + {'title': 'Voltage Min:', 'name': 'volt_min', 'type': 'list', 'value': -10., }, + {'title': 'Voltage Max:', 'name': 'volt_max', 'type': 'list', 'value': 10., }, + ]}, + {'title': 'Current:', 'name': 'current_settings', 'type': 'group', 'visible': False, 'children': [ + {'title': 'Current Min:', 'name': 'curr_min', 'type': 'float', 'value': -1, 'suffix': 'A'}, + {'title': 'Current Max:', 'name': 'curr_max', 'type': 'float', 'value': 1, 'suffix': 'A'}, + ]}, + ] + + def __init__(self, **opts): + opts['type'] = 'groupao' + opts['addText'] = "Add" + opts['addList'] = opts['limits'] + GroupParameter.__init__(self, **opts) + + def addNew(self, typ=None): + """ + Add a child. + + =============== =========== + **Parameters** **Type** + *typ* string + =============== =========== + """ + childnames = [par.name() for par in self.children()] + if childnames == []: + newindex = 0 + else: + newindex = len(childnames) + + child = {'title': typ, 'name': 'ao{:02.0f}'.format(newindex), 'type': 'group', 'children': self.params, + 'removable': True, 'renamable': False} + + self.addChild(child) + + +registerParameterType('groupao', ScalableGroupAO, override=True) + + +class ScalableGroupCounter(GroupParameter): + """ + | + + ================ ============= + **Attributes** **Type** + *opts* dictionnary + ================ ============= + + See Also + -------- + hardware.DAQ_Move_Stage_type + """ + + params = [{'title': 'Edge type:', 'name': 'edge', 'type': 'list', 'limits': [e.name for e in Edge]}, ] + + def __init__(self, **opts): + opts['type'] = 'groupcounter' + opts['addText'] = "Add" + opts['addList'] = opts['limits'] + GroupParameter.__init__(self, **opts) + + def addNew(self, typ=None): + """ + Add a child. + + =============== =========== + **Parameters** **Type** + *typ* string + =============== =========== + """ + childnames = [par.name() for par in self.children()] + if childnames == []: + newindex = 0 + else: + newindex = len(childnames) + + child = {'title': typ, 'name': 'counter{:02.0f}'.format(newindex), 'type': 'group', 'children': self.params, + 'removable': True, 'renamable': False} + + self.addChild(child) + + +registerParameterType('groupcounter', ScalableGroupCounter, override=True) + + +class ScalableGroupDI(GroupParameter): + """ + """ + + params = [] + + def __init__(self, **opts): + opts['type'] = 'groupdi' + opts['addText'] = "Add" + opts['addList'] = opts['limits'] + GroupParameter.__init__(self, **opts) + + def addNew(self, typ=None): + """ + Add a child. + + =============== =========== + **Parameters** **Type** + *typ* string + =============== =========== + """ + childnames = [par.name() for par in self.children()] + if childnames == []: + newindex = 0 + else: + newindex = len(childnames) + + child = {'title': typ, 'name': 'di{:02.0f}'.format(newindex), 'type': 'group', 'children': self.params, + 'removable': True, 'renamable': False} + self.addChild(child) + + +registerParameterType('groupdi', ScalableGroupDI, override=True) + + +class ScalableGroupDO(GroupParameter): + """ + """ + + params = [] + + def __init__(self, **opts): + opts['type'] = 'groupdo' + opts['addText'] = "Add" + opts['addList'] = opts['limits'] + GroupParameter.__init__(self, **opts) + + def addNew(self, typ=None): + """ + Add a child. + + =============== =========== + **Parameters** **Type** + *typ* string + =============== =========== + """ + childnames = [par.name() for par in self.children()] + if childnames == []: + newindex = 0 + else: + newindex = len(childnames) + + child = {'title': typ, 'name': 'counter{:02.0f}'.format(newindex), 'type': 'group', 'children': self.params, + 'removable': True, 'renamable': False} + self.addChild(child) + + +registerParameterType('groupdo', ScalableGroupDO, override=True) + + +class DAQ_NIDAQmx_base: + """ + Base NIDAQmx class for using DAQmx objects from daqmxni.py in the DAQ_NIDAQmx_Move & DAQ_NIDAQmx_Viewer + """ + data_grabed_signal = Signal(list) + param_instance = NIDAQmx() + params = [{'title': 'Refresh hardware:', 'name': 'refresh_hardware', 'type': 'bool', 'value': False}, + {'title': 'Signal type:', 'name': 'NIDAQ_type', 'type': 'list', + 'limits': [Ds.name for Ds in ChannelType]}, + {'title': 'NSamples To Read', 'name': 'nsamplestoread', 'type': 'int', 'value': 1000, 'default': 1000, + 'min': 1}, + {'title': 'AO Settings:', 'name': 'ao_settings', 'type': 'group', 'children': [ + {'title': 'Waveform:', 'name': 'waveform', 'type': 'list', 'value': 'DC', + 'limits': ['DC', 'Sinus', 'Ramp']}, + {'title': 'Controlled param:', 'name': 'cont_param', 'type': 'list', 'value': 'offset', + 'limits': ['offset', 'amplitude', 'frequency']}, + {'title': 'Waveform Settings:', 'name': 'waveform_settings', 'type': 'group', 'visible': False, + 'children': [ + {'title': 'Offset:', 'name': 'offset', 'type': 'float', 'value': 0., }, + {'title': 'Amplitude:', 'name': 'amplitude', 'type': 'float', 'value': 1., }, + {'title': 'Frequency:', 'name': 'frequency', 'type': 'float', 'value': 10., }, + ]}, + ]}, + {'title': 'Clock Settings:', 'name': 'clock_settings', 'type': 'group', 'children': [ + {'title': 'Nsamples:', 'name': 'Nsamples', 'type': 'int', 'value': 1000, 'default': 1000, 'min': 1}, + {'title': 'Frequency:', 'name': 'frequency', 'type': 'float', 'value': 1000., 'default': 1000., + 'min': 0., 'suffix': 'Hz'}, + {'title': 'Repetition?:', 'name': 'repetition', 'type': 'bool', 'value': False, }, + ] + }, + {'title': 'AI Channels:', 'name': 'ai_channels', 'type': 'groupai', + 'limits': NIDAQmx.get_NIDAQ_channels(source_type=ChannelType.ANALOG_INPUT)}, + {'title': 'AO Channels:', 'name': 'ao_channels', 'type': 'groupao', + 'limits': NIDAQmx.get_NIDAQ_channels(source_type=ChannelType.ANALOG_OUTPUT)}, + {'title': 'DO Channels:', 'name': 'do_channels', 'type': 'groupdo', + 'limits': NIDAQmx.get_NIDAQ_channels(source_type=ChannelType.DIGITAL_OUTPUT)}, + {'title': 'DI Channels:', 'name': 'di_channels', 'type': 'groupdi', + 'limits': NIDAQmx.get_NIDAQ_channels(source_type=ChannelType.DIGITAL_INPUT)}, + {'title': 'Counter Settings:', 'name': 'counter_settings', 'type': 'group', 'visible': True, 'children': [ + {'title': 'Counting time (ms):', 'name': 'counting_time', 'type': 'float', 'value': 100., + 'default': 100., 'min': 0.}, + {'title': 'CI Channels:', 'name': 'ci_channels', 'type': 'groupcounter', + 'limits': NIDAQmx.get_NIDAQ_channels(source_type=ChannelType.COUNTER_INPUT)}, + {'title': 'CO Channels:', 'name': 'co_channels', 'type': 'groupcounter', + 'limits': NIDAQmx.get_NIDAQ_channels(source_type=ChannelType.COUNTER_OUTPUT)}, + ]}, + {'title': 'Trigger Settings:', 'name': 'trigger_settings', 'type': 'group', 'visible': True, 'children': [ + {'title': 'Enable?:', 'name': 'enable', 'type': 'bool', 'value': False, }, + {'title': 'Trigger Source:', 'name': 'trigger_channel', 'type': 'list', + 'limits': NIDAQmx.getTriggeringSources()}, + {'title': 'Edge type:', 'name': 'edge', 'type': 'list', 'limits': [e.name for e in Edge], + 'visible': False}, + {'title': 'Level:', 'name': 'level', 'type': 'float', 'value': 1., 'visible': False} + ]} + ] + + def __init__(self): + super().__init__() + + self.timer = None + self.channels = None + self.clock_settings = None + self.trigger_settings = None + self.live = False + + def commit_settings(self, param: Parameter): + """ + Activate the parameters changes in the hardware. + + =============== ================================ =========================== + **Parameters** **Type** **Description** + *param* instance of pyqtgraph.parameter the parameter to activate + =============== ================================ =========================== + + See Also + -------- + update_NIDAQ_channels, update_task, DAQ_NIDAQ_source, refresh_hardware + """ + if param.name() == 'NIDAQ_devices': + self.controller.update_NIDAQ_channels() + + if param.name() == 'NIDAQ_type': + self.controller.update_NIDAQ_channels(param.value()) + if param.value() == ChannelType.ANALOG_INPUT.name: # analog input + self.settings.child('clock_settings').show() + self.settings.child('ai_channels').show() + self.settings.child('ao_channels').hide() + self.settings.child('ao_settings').hide() + self.settings.child('counter_settings').hide() + self.settings.child('do_channels').hide() + self.settings.child('di_channels').hide() + + elif param.value() == ChannelType.ANALOG_OUTPUT.name: # analog output + self.settings.child('clock_settings').show() + self.settings.child('ai_channels').hide() + self.settings.child('ao_channels').show() + self.settings.child('ao_settings').show() + self.settings.child('counter_settings').hide() + self.settings.child('do_channels').hide() + self.settings.child('di_channels').hide() + + elif param.value() == ChannelType.COUNTER_INPUT.name: # counter input + self.settings.child('clock_settings').hide() + self.settings.child('ai_channels').hide() + self.settings.child('ao_channels').hide() + self.settings.child('ao_settings').hide() + self.settings.child('counter_settings', 'ci_channels').show() + self.settings.child('do_channels').hide() + self.settings.child('di_channels').hide() + + elif param.value() == ChannelType.COUNTER_OUTPUT.name: # counter output + self.settings.child('clock_settings').hide() + self.settings.child('ai_channels').hide() + self.settings.child('ao_channels').hide() + self.settings.child('ao_settings').hide() + self.settings.child('counter_settings', 'co_channels').show() + self.settings.child('do_channels').hide() + self.settings.child('di_channels').hide() + + elif param.value() == ChannelType.DIGITAL_INPUT.name: # Digital_Input + self.settings.child('clock_settings').show() + self.settings.child('ai_channels').hide() + self.settings.child('ao_channels').show() + self.settings.child('ao_settings').show() + self.settings.child('counter_settings').hide() + self.settings.child('do_channels').hide() + self.settings.child('di_channels').show() + + elif param.value() == ChannelType.DIGITAL_OUTPUT.name: # digital output + self.settings.child('clock_settings').show() + self.settings.child('ai_channels').hide() + self.settings.child('ao_channels').hide() + self.settings.child('ao_settings').hide() + self.settings.child('counter_settings').hide() + self.settings.child('do_channels').show() + self.settings.child('di_channels').hide() + + elif param.name() == 'refresh_hardware': + if param.value(): + self.controller.refresh_hardware() + QtWidgets.QApplication.processEvents() + self.settings.child('refresh_hardware').setValue(False) + + elif param.name() == 'ai_type': + param.parent().child('voltage_settings').show(param.value() == UsageTypeAI.VOLTAGE.name) + param.parent().child('current_settings').show(param.value() == UsageTypeAI.CURRENT.name) + param.parent().child('thermoc_settings').show(param.value() == UsageTypeAI.TEMPERATURE_THERMOCOUPLE.name) + + elif param.name() == 'ao_type': + param.parent().child('voltage_settings').show(param.value() == UsageTypeAI.VOLTAGE.name) + param.parent().child('current_settings').show(param.value() == UsageTypeAI.CURRENT.name) + + elif param.name() == 'trigger_channel': + param.parent().child('level').show('PF' not in param.opts['title']) + + def update_task(self): + self.channels = self.get_channels_from_settings() + self.clock_settings = ClockSettings(frequency=self.settings['clock_settings', 'frequency'], + Nsamples=self.settings['clock_settings', 'Nsamples'], + edge=Edge.RISING, + repetition=self.live, ) + self.trigger_settings = \ + TriggerSettings(trig_source=self.settings['trigger_settings', 'trigger_channel'], + enable=self.settings['trigger_settings', 'enable'], + edge=Edge[self.settings['trigger_settings', 'edge']], + level=self.settings['trigger_settings', 'level'], ) + if self.channels: + self.controller.update_task(self.channels, self.clock_settings, trigger_settings=self.trigger_settings) + + def get_channels_from_settings(self): + channels = [] + if self.settings['NIDAQ_type'] == ChannelType.ANALOG_INPUT.name: # analog input + source = ChannelType.ANALOG_INPUT + for channel in self.settings.child('ai_channels').children(): + analog_type = UsageTypeAI[channel['ai_type']] + if analog_type == UsageTypeAI.VOLTAGE: + channels.append(AIChannel(name=channel.opts['title'], + source=source, analog_type=analog_type, + value_min=channel['voltage_settings', 'volt_min'], + value_max=channel['voltage_settings', 'volt_max'], + termination=TerminalConfiguration[channel['termination']], )) + elif analog_type == UsageTypeAI.CURRENT: + channels.append(AIChannel(name=channel.opts['title'], + source=source, analog_type=analog_type, + value_min=channel['current_settings', 'curr_min'], + value_max=channel['current_settings', 'curr_max'], + termination=TerminalConfiguration[channel['termination']], )) + elif analog_type == UsageTypeAI.TEMPERATURE_THERMOCOUPLE: + channels.append(AIThermoChannel(name=channel.opts['title'], + source=source, analog_type=analog_type, + value_min=channel['thermoc_settings', 'T_min'], + value_max=channel['thermoc_settings', 'T_max'], + thermo_type=ThermocoupleType[ + channel['thermoc_settings', 'thermoc_type']], )) + + elif self.settings['NIDAQ_type'] == ChannelType.ANALOG_OUTPUT.name: # analog output + source = ChannelType.ANALOG_OUTPUT + for channel in self.settings.child('ao_channels').children(): + analog_type = UsageTypeAO[channel['ao_type']] + channels.append(AOChannel(name=channel.opts['title'], + source=source, analog_type=analog_type, + value_min=channel['voltage_settings', 'volt_min'], + value_max=channel['voltage_settings', 'volt_max'], + )) + + elif self.settings['NIDAQ_type'] == ChannelType.COUNTER_INPUT.name: # counter input + source = ChannelType.COUNTER_INPUT + for channel in self.settings.child('counter_settings', 'ci_channels').children(): + channels.append(CIChannel(name=channel.opts['title'], + source=source, edge=Edge[channel['edge']])) + + elif self.settings['NIDAQ_type'] == ChannelType.COUNTER_OUTPUT.name: # counter output + source = ChannelType.COUNTER_OUTPUT + for channel in self.settings.child('counter_settings', 'co_channels').children(): + channels.append(COChannel(name=channel.opts['title'], + source=source, edge=Edge[channel['edge']])) + + elif self.settings['NIDAQ_type'] == ChannelType.DIGITAL_INPUT.name: # digital input + source = ChannelType.DIGITAL_INPUT + for channel in self.settings.child('di_channels').children(): + channels.append(DIChannel(name=channel.opts['title'], + source=source)) + + elif self.settings['NIDAQ_type'] == ChannelType.DIGITAL_OUTPUT.name: # Digital output + source = ChannelType.DIGITAL_OUTPUT + for channel in self.settings.child('do_channels').children(): + channels.append(DOChannel(name=channel.opts['title'], + source=source)) + + channels = [ch for ch in channels if self.settings.child("devices").value() in ch.name] + return channels + + def stop(self): + """ + """ + if not not self.timer: + self.timer.stop() + QtWidgets.QApplication.processEvents() + self.controller.stop() + + + diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py index 7218073..003479e 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx.py @@ -1,18 +1,23 @@ -from qtpy import QtWidgets -from qtpy.QtCore import Signal -from pymodaq.utils.logger import set_logger, get_module_name +from qtpy import QtWidgets, QtCore +from qtpy.QtCore import Signal, QThread +from pymodaq.utils.daq_utils import ThreadCommand, getLineInfo +from pymodaq.utils.data import DataFromPlugins, Axis, DataActuator, DataToExport +import numpy as np +from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base +from pymodaq.control_modules.move_utility_classes import DAQ_Move_base +from easydict import EasyDict as edict + +from pymodaq.control_modules.viewer_utility_classes import comon_parameters as viewer_params +from pymodaq.control_modules.move_utility_classes import comon_parameters_fun as actuator_params + from pymodaq.utils.parameter import Parameter from pymodaq.utils.parameter.pymodaq_ptypes import registerParameterType, GroupParameter -from pymodaq_plugins_daqmx.hardware.national_instruments.daqmxni import NIDAQmx, Edge, ChannelType, ClockSettings, \ - AIChannel, AIThermoChannel, AOChannel, CIChannel, COChannel, DOChannel, DIChannel, UsageTypeAI, UsageTypeAO, \ - ThermocoupleType, TerminalConfiguration, TriggerSettings - -logger = set_logger(get_module_name(__file__)) +from .daqmx import DAQmx, DAQ_analog_types, DAQ_thermocouples, DAQ_termination, Edge, DAQ_NIDAQ_source, \ + ClockSettings, AIChannel, Counter, AIThermoChannel, AOChannel, TriggerSettings, DOChannel, DIChannel class ScalableGroupAI(GroupParameter): - """ | @@ -26,23 +31,22 @@ class ScalableGroupAI(GroupParameter): hardware.DAQ_Move_Stage_type """ - params = [{'title': 'AI type:', 'name': 'ai_type', 'type': 'list', 'limits': [Uai.name for Uai in UsageTypeAI]}, - {'title': 'Voltage:', 'name': 'voltage_settings', 'type': 'group', 'children': [ - {'title': 'Voltage Min:', 'name': 'volt_min', 'type': 'float', 'value': -10.}, - {'title': 'Voltage Max:', 'name': 'volt_max', 'type': 'float', 'value': 10.}, + params = [{'title': 'AI type:', 'name': 'ai_type', 'type': 'list', 'limits': DAQ_analog_types.names()}, + {'title': 'Voltages:', 'name': 'voltage_settings', 'type': 'group', 'children': [ + {'title': 'Voltage Min:', 'name': 'volt_min', 'type': 'list', 'value': -10.}, + {'title': 'Voltage Max:', 'name': 'volt_max', 'type': 'list', 'value': 10.}, ]}, {'title': 'Current:', 'name': 'current_settings', 'type': 'group', 'visible': False, 'children': [ {'title': 'Current Min:', 'name': 'curr_min', 'type': 'float', 'value': -1, 'suffix': 'A'}, {'title': 'Current Max:', 'name': 'curr_max', 'type': 'float', 'value': 1, 'suffix': 'A'}, ]}, {'title': 'Thermocouple:', 'name': 'thermoc_settings', 'type': 'group', 'visible': False, 'children': [ - {'title': 'Thc. type:', 'name': 'thermoc_type', 'type': 'list', - 'limits': [Th.name for Th in ThermocoupleType], 'value': 'K'}, + {'title': 'Thc. type:', 'name': 'thermoc_type', 'type': 'list', 'limits': DAQ_thermocouples.names(), + 'value': 'K'}, {'title': 'Temp. Min (°C):', 'name': 'T_min', 'type': 'float', 'value': 0, 'suffix': '°C'}, {'title': 'Temp. Max (°C):', 'name': 'T_max', 'type': 'float', 'value': 50, 'suffix': '°C'}, ]}, - {'title': 'Termination:', 'name': 'termination', 'type': 'list', - 'limits': [Te.name for Te in TerminalConfiguration]}, + {'title': 'Termination:', 'name': 'termination', 'type': 'list', 'limits': DAQ_termination.names()}, ] def __init__(self, **opts): @@ -51,7 +55,7 @@ def __init__(self, **opts): opts['addList'] = opts['limits'] GroupParameter.__init__(self, **opts) - def addNew(self, typ=None): + def addNew(self, typ): """ Add a child. @@ -70,8 +74,6 @@ def addNew(self, typ=None): 'removable': True, 'renamable': False} self.addChild(child) - - registerParameterType('groupai', ScalableGroupAI, override=True) @@ -89,7 +91,7 @@ class ScalableGroupAO(GroupParameter): hardware.DAQ_Move_Stage_type """ - params = [{'title': 'AO type:', 'name': 'ao_type', 'type': 'list', 'limits': [Uao.name for Uao in UsageTypeAO]}, + params = [{'title': 'AO type:', 'name': 'ao_type', 'type': 'list', 'limits': DAQ_analog_types.names()[0:2]}, {'title': 'Voltages:', 'name': 'voltage_settings', 'type': 'group', 'children': [ {'title': 'Voltage Min:', 'name': 'volt_min', 'type': 'list', 'value': -10., }, {'title': 'Voltage Max:', 'name': 'volt_max', 'type': 'list', 'value': 10., }, @@ -106,7 +108,7 @@ def __init__(self, **opts): opts['addList'] = opts['limits'] GroupParameter.__init__(self, **opts) - def addNew(self, typ=None): + def addNew(self, typ): """ Add a child. @@ -125,8 +127,6 @@ def addNew(self, typ=None): 'removable': True, 'renamable': False} self.addChild(child) - - registerParameterType('groupao', ScalableGroupAO, override=True) @@ -144,7 +144,7 @@ class ScalableGroupCounter(GroupParameter): hardware.DAQ_Move_Stage_type """ - params = [{'title': 'Edge type:', 'name': 'edge', 'type': 'list', 'limits': [e.name for e in Edge]}, ] + params = [{'title': 'Edge type:', 'name': 'edge', 'type': 'list', 'limits': Edge.names()}, ] def __init__(self, **opts): opts['type'] = 'groupcounter' @@ -152,7 +152,7 @@ def __init__(self, **opts): opts['addList'] = opts['limits'] GroupParameter.__init__(self, **opts) - def addNew(self, typ=None): + def addNew(self, typ): """ Add a child. @@ -171,8 +171,6 @@ def addNew(self, typ=None): 'removable': True, 'renamable': False} self.addChild(child) - - registerParameterType('groupcounter', ScalableGroupCounter, override=True) @@ -188,7 +186,7 @@ def __init__(self, **opts): opts['addList'] = opts['limits'] GroupParameter.__init__(self, **opts) - def addNew(self, typ=None): + def addNew(self, typ): """ Add a child. @@ -206,8 +204,6 @@ def addNew(self, typ=None): child = {'title': typ, 'name': 'di{:02.0f}'.format(newindex), 'type': 'group', 'children': self.params, 'removable': True, 'renamable': False} self.addChild(child) - - registerParameterType('groupdi', ScalableGroupDI, override=True) @@ -223,7 +219,7 @@ def __init__(self, **opts): opts['addList'] = opts['limits'] GroupParameter.__init__(self, **opts) - def addNew(self, typ=None): + def addNew(self, typ): """ Add a child. @@ -241,66 +237,54 @@ def addNew(self, typ=None): child = {'title': typ, 'name': 'counter{:02.0f}'.format(newindex), 'type': 'group', 'children': self.params, 'removable': True, 'renamable': False} self.addChild(child) - - registerParameterType('groupdo', ScalableGroupDO, override=True) -class DAQ_NIDAQmx_base: - """ - Base NIDAQmx class for using DAQmx objects from daqmxni.py in the DAQ_NIDAQmx_Move & DAQ_NIDAQmx_Viewer - """ +class DAQ_NIDAQmx_base(DAQmx): data_grabed_signal = Signal(list) - param_instance = NIDAQmx() - params = [{'title': 'Refresh hardware:', 'name': 'refresh_hardware', 'type': 'bool', 'value': False}, - {'title': 'Signal type:', 'name': 'NIDAQ_type', 'type': 'list', - 'limits': [Ds.name for Ds in ChannelType]}, - {'title': 'NSamples To Read', 'name': 'nsamplestoread', 'type': 'int', 'value': 1000, 'default': 1000, - 'min': 1}, - {'title': 'AO Settings:', 'name': 'ao_settings', 'type': 'group', 'children': [ - {'title': 'Waveform:', 'name': 'waveform', 'type': 'list', 'value': 'DC', - 'limits': ['DC', 'Sinus', 'Ramp']}, - {'title': 'Controlled param:', 'name': 'cont_param', 'type': 'list', 'value': 'offset', - 'limits': ['offset', 'amplitude', 'frequency']}, - {'title': 'Waveform Settings:', 'name': 'waveform_settings', 'type': 'group', 'visible': False, - 'children': [ - {'title': 'Offset:', 'name': 'offset', 'type': 'float', 'value': 0., }, - {'title': 'Amplitude:', 'name': 'amplitude', 'type': 'float', 'value': 1., }, - {'title': 'Frequency:', 'name': 'frequency', 'type': 'float', 'value': 10., }, - ]}, - ]}, - {'title': 'Clock Settings:', 'name': 'clock_settings', 'type': 'group', 'children': [ - {'title': 'Nsamples:', 'name': 'Nsamples', 'type': 'int', 'value': 1000, 'default': 1000, 'min': 1}, - {'title': 'Frequency:', 'name': 'frequency', 'type': 'float', 'value': 1000., 'default': 1000., - 'min': 0., 'suffix': 'Hz'}, - {'title': 'Repetition?:', 'name': 'repetition', 'type': 'bool', 'value': False, }, - ] - }, - {'title': 'AI Channels:', 'name': 'ai_channels', 'type': 'groupai', - 'limits': NIDAQmx.get_NIDAQ_channels(source_type=ChannelType.ANALOG_INPUT)}, - {'title': 'AO Channels:', 'name': 'ao_channels', 'type': 'groupao', - 'limits': NIDAQmx.get_NIDAQ_channels(source_type=ChannelType.ANALOG_OUTPUT)}, - {'title': 'DO Channels:', 'name': 'do_channels', 'type': 'groupdo', - 'limits': NIDAQmx.get_NIDAQ_channels(source_type=ChannelType.DIGITAL_OUTPUT)}, - {'title': 'DI Channels:', 'name': 'di_channels', 'type': 'groupdi', - 'limits': NIDAQmx.get_NIDAQ_channels(source_type=ChannelType.DIGITAL_INPUT)}, - {'title': 'Counter Settings:', 'name': 'counter_settings', 'type': 'group', 'visible': True, 'children': [ - {'title': 'Counting time (ms):', 'name': 'counting_time', 'type': 'float', 'value': 100., - 'default': 100., 'min': 0.}, - {'title': 'CI Channels:', 'name': 'ci_channels', 'type': 'groupcounter', - 'limits': NIDAQmx.get_NIDAQ_channels(source_type=ChannelType.COUNTER_INPUT)}, - {'title': 'CO Channels:', 'name': 'co_channels', 'type': 'groupcounter', - 'limits': NIDAQmx.get_NIDAQ_channels(source_type=ChannelType.COUNTER_OUTPUT)}, - ]}, - {'title': 'Trigger Settings:', 'name': 'trigger_settings', 'type': 'group', 'visible': True, 'children': [ - {'title': 'Enable?:', 'name': 'enable', 'type': 'bool', 'value': False, }, - {'title': 'Trigger Source:', 'name': 'trigger_channel', 'type': 'list', - 'limits': NIDAQmx.getTriggeringSources()}, - {'title': 'Edge type:', 'name': 'edge', 'type': 'list', 'limits': [e.name for e in Edge], - 'visible': False}, - {'title': 'Level:', 'name': 'level', 'type': 'float', 'value': 1., 'visible': False} - ]} - ] + + params =[{'title': 'Refresh hardware:', 'name': 'refresh_hardware', 'type': 'bool', 'value': False}, + {'title': 'Signal type:', 'name': 'NIDAQ_type', 'type': 'list', 'limits': DAQ_NIDAQ_source.names()}, + {'title': 'AO Settings:', 'name': 'ao_settings', 'type': 'group', 'children': [ + {'title': 'Waveform:', 'name': 'waveform', 'type': 'list', 'value': 'DC', 'limits': ['DC', 'Sinus', 'Ramp']}, + + {'title': 'Controlled param:', 'name': 'cont_param', 'type': 'list', 'value': 'offset', + 'limits': ['offset', 'amplitude', 'frequency']}, + {'title': 'Waveform Settings:', 'name': 'waveform_settings', 'type': 'group', 'visible': False, 'children': [ + {'title': 'Offset:', 'name': 'offset', 'type': 'float', 'value': 0., }, + {'title': 'Amplitude:', 'name': 'amplitude', 'type': 'float', 'value': 1., }, + {'title': 'Frequency:', 'name': 'frequency', 'type': 'float', 'value': 10., }, + ]}, + ]}, + {'title': 'Clock Settings:', 'name': 'clock_settings', 'type': 'group', 'children': [ + {'title': 'Nsamples:', 'name': 'Nsamples', 'type': 'int', 'value': 1000, 'default': 1000, 'min': 1}, + {'title': 'Frequency:', 'name': 'frequency', 'type': 'float', 'value': 1000., 'default': 1000., + 'min': 0., 'suffix': 'Hz'}, + {'title': 'Repetition?:', 'name': 'repetition', 'type': 'bool', 'value': False, }, + ] + }, + {'title': 'AI Channels:', 'name': 'ai_channels', 'type': 'groupai', + 'limits': DAQmx.get_NIDAQ_channels(source_type='Analog_Input')}, + {'title': 'AO Channels:', 'name': 'ao_channels', 'type': 'groupao', + 'limits': DAQmx.get_NIDAQ_channels(source_type='Analog_Output')}, + {'title': 'DO Channels:', 'name': 'do_channels', 'type': 'groupdo', + 'limits': DAQmx.get_NIDAQ_channels(source_type='Digital_Output')}, + {'title': 'DI Channels:', 'name': 'di_channels', 'type': 'groupdi', + 'limits': DAQmx.get_NIDAQ_channels(source_type='Digital_Input')}, + {'title': 'Counter Settings:', 'name': 'counter_settings', 'type': 'group', 'visible': True, 'children': [ + {'title': 'Counting time (ms):', 'name': 'counting_time', 'type': 'float', 'value': 100., + 'default': 100., 'min': 0.}, + {'title': 'Counting Channels:', 'name': 'counter_channels', 'type': 'groupcounter', + 'limits': DAQmx.get_NIDAQ_channels(source_type='Counter')}, + ]}, + {'title': 'Trigger Settings:', 'name': 'trigger_settings', 'type': 'group', 'visible': True, 'children': [ + {'title': 'Enable?:', 'name': 'enable', 'type': 'bool', 'value': False, }, + {'title': 'Trigger Source:', 'name': 'trigger_channel', 'type': 'list', + 'limits': DAQmx.getTriggeringSources()}, + {'title': 'Edge type:', 'name': 'edge', 'type': 'list', 'limits': Edge.names(), 'visible': False}, + {'title': 'Level:', 'name': 'level', 'type': 'float', 'value': 1., 'visible': False} + ]} + ] def __init__(self): super().__init__() @@ -310,6 +294,7 @@ def __init__(self): self.clock_settings = None self.trigger_settings = None self.live = False + self.refresh_hardware() def commit_settings(self, param: Parameter): """ @@ -324,12 +309,13 @@ def commit_settings(self, param: Parameter): -------- update_NIDAQ_channels, update_task, DAQ_NIDAQ_source, refresh_hardware """ - if param.name() == 'NIDAQ_devices': - self.controller.update_NIDAQ_channels() + # if param.name() == 'NIDAQ_devices': + # self.update_NIDAQ_channels() + # self.update_task() if param.name() == 'NIDAQ_type': - self.controller.update_NIDAQ_channels(param.value()) - if param.value() == ChannelType.ANALOG_INPUT.name: # analog input + self.update_NIDAQ_channels(param.value()) + if param.value() == DAQ_NIDAQ_source(0).name: #analog input self.settings.child('clock_settings').show() self.settings.child('ai_channels').show() self.settings.child('ao_channels').hide() @@ -338,43 +324,25 @@ def commit_settings(self, param: Parameter): self.settings.child('do_channels').hide() self.settings.child('di_channels').hide() - elif param.value() == ChannelType.ANALOG_OUTPUT.name: # analog output - self.settings.child('clock_settings').show() - self.settings.child('ai_channels').hide() - self.settings.child('ao_channels').show() - self.settings.child('ao_settings').show() - self.settings.child('counter_settings').hide() - self.settings.child('do_channels').hide() - self.settings.child('di_channels').hide() - - elif param.value() == ChannelType.COUNTER_INPUT.name: # counter input - self.settings.child('clock_settings').hide() - self.settings.child('ai_channels').hide() - self.settings.child('ao_channels').hide() - self.settings.child('ao_settings').hide() - self.settings.child('counter_settings', 'ci_channels').show() - self.settings.child('do_channels').hide() - self.settings.child('di_channels').hide() - - elif param.value() == ChannelType.COUNTER_OUTPUT.name: # counter output + elif param.value() == DAQ_NIDAQ_source(1).name: #counter input self.settings.child('clock_settings').hide() self.settings.child('ai_channels').hide() self.settings.child('ao_channels').hide() self.settings.child('ao_settings').hide() - self.settings.child('counter_settings', 'co_channels').show() + self.settings.child('counter_settings').show() self.settings.child('do_channels').hide() self.settings.child('di_channels').hide() - elif param.value() == ChannelType.DIGITAL_INPUT.name: # Digital_Input + elif param.value() == DAQ_NIDAQ_source(2).name: #analog output self.settings.child('clock_settings').show() self.settings.child('ai_channels').hide() self.settings.child('ao_channels').show() self.settings.child('ao_settings').show() self.settings.child('counter_settings').hide() self.settings.child('do_channels').hide() - self.settings.child('di_channels').show() + self.settings.child('di_channels').hide() - elif param.value() == ChannelType.DIGITAL_OUTPUT.name: # digital output + elif param.value() == DAQ_NIDAQ_source(3).name: # digital output self.settings.child('clock_settings').show() self.settings.child('ai_channels').hide() self.settings.child('ao_channels').hide() @@ -383,99 +351,100 @@ def commit_settings(self, param: Parameter): self.settings.child('do_channels').show() self.settings.child('di_channels').hide() + elif param.value() == DAQ_NIDAQ_source(4).name: # Digital_Input + self.settings.child('clock_settings').show() + self.settings.child('ai_channels').hide() + self.settings.child('ao_channels').show() + self.settings.child('ao_settings').show() + self.settings.child('counter_settings').hide() + self.settings.child('do_channels').hide() + self.settings.child('di_channels').show() + self.update_task() + elif param.name() == 'refresh_hardware': if param.value(): - self.controller.refresh_hardware() + self.refresh_hardware() QtWidgets.QApplication.processEvents() self.settings.child('refresh_hardware').setValue(False) elif param.name() == 'ai_type': - param.parent().child('voltage_settings').show(param.value() == UsageTypeAI.VOLTAGE.name) - param.parent().child('current_settings').show(param.value() == UsageTypeAI.CURRENT.name) - param.parent().child('thermoc_settings').show(param.value() == UsageTypeAI.TEMPERATURE_THERMOCOUPLE.name) + param.parent().child('voltage_settings').show(param.value() == 'Voltage') + param.parent().child('current_settings').show(param.value() == 'Current') + param.parent().child('thermoc_settings').show(param.value() == 'Thermocouple') + self.update_task() elif param.name() == 'ao_type': - param.parent().child('voltage_settings').show(param.value() == UsageTypeAI.VOLTAGE.name) - param.parent().child('current_settings').show(param.value() == UsageTypeAI.CURRENT.name) + param.parent().child('voltage_settings').show(param.value() == 'Voltage') + param.parent().child('current_settings').show(param.value() == 'Current') + self.update_task() elif param.name() == 'trigger_channel': param.parent().child('level').show('PF' not in param.opts['title']) + else: + self.update_task() + def update_task(self): self.channels = self.get_channels_from_settings() self.clock_settings = ClockSettings(frequency=self.settings['clock_settings', 'frequency'], Nsamples=self.settings['clock_settings', 'Nsamples'], - edge=Edge.RISING, - repetition=self.live, ) + repetition=self.live,) self.trigger_settings = \ TriggerSettings(trig_source=self.settings['trigger_settings', 'trigger_channel'], enable=self.settings['trigger_settings', 'enable'], - edge=Edge[self.settings['trigger_settings', 'edge']], - level=self.settings['trigger_settings', 'level'], ) - if self.channels: - self.controller.update_task(self.channels, self.clock_settings, trigger_settings=self.trigger_settings) + edge=self.settings['trigger_settings', 'edge'], + level=self.settings['trigger_settings', 'level'],) + + if not not self.channels: + super().update_task(self.channels, self.clock_settings, trigger_settings=self.trigger_settings) + def get_channels_from_settings(self): channels = [] - if self.settings['NIDAQ_type'] == ChannelType.ANALOG_INPUT.name: # analog input - source = ChannelType.ANALOG_INPUT + if self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(0).name: # analog input for channel in self.settings.child('ai_channels').children(): - analog_type = UsageTypeAI[channel['ai_type']] - if analog_type == UsageTypeAI.VOLTAGE: + analog_type = channel['ai_type'] + if analog_type == 'Voltage': channels.append(AIChannel(name=channel.opts['title'], - source=source, analog_type=analog_type, + source='Analog_Input', analog_type=analog_type, value_min=channel['voltage_settings', 'volt_min'], value_max=channel['voltage_settings', 'volt_max'], - termination=TerminalConfiguration[channel['termination']], )) - elif analog_type == UsageTypeAI.CURRENT: + termination=channel['termination'],)) + elif analog_type == 'Current': channels.append(AIChannel(name=channel.opts['title'], - source=source, analog_type=analog_type, + source='Analog_Input', analog_type=analog_type, value_min=channel['current_settings', 'curr_min'], value_max=channel['current_settings', 'curr_max'], - termination=TerminalConfiguration[channel['termination']], )) - elif analog_type == UsageTypeAI.TEMPERATURE_THERMOCOUPLE: + termination=channel['termination'], )) + elif analog_type == 'Thermocouple': channels.append(AIThermoChannel(name=channel.opts['title'], - source=source, analog_type=analog_type, - value_min=channel['thermoc_settings', 'T_min'], - value_max=channel['thermoc_settings', 'T_max'], - thermo_type=ThermocoupleType[ - channel['thermoc_settings', 'thermoc_type']], )) - - elif self.settings['NIDAQ_type'] == ChannelType.ANALOG_OUTPUT.name: # analog output - source = ChannelType.ANALOG_OUTPUT + source='Analog_Input', analog_type=analog_type, + value_min=channel['thermoc_settings', 'T_min'], + value_max=channel['thermoc_settings', 'T_max'], + termination=channel['termination'], + thermo_type=channel['thermoc_settings', 'thermoc_type'],)) + + elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(1).name: # counter input + for channel in self.settings.child('counter_settings', 'counter_channels').children(): + channels.append(Counter(name=channel.opts['title'], + source='Counter', edge=channel['edge'])) + + elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(2).name: # analog output for channel in self.settings.child('ao_channels').children(): - analog_type = UsageTypeAO[channel['ao_type']] + analog_type = channel['ao_type'] channels.append(AOChannel(name=channel.opts['title'], - source=source, analog_type=analog_type, + source='Analog_Output', analog_type=analog_type, value_min=channel['voltage_settings', 'volt_min'], value_max=channel['voltage_settings', 'volt_max'], )) - - elif self.settings['NIDAQ_type'] == ChannelType.COUNTER_INPUT.name: # counter input - source = ChannelType.COUNTER_INPUT - for channel in self.settings.child('counter_settings', 'ci_channels').children(): - channels.append(CIChannel(name=channel.opts['title'], - source=source, edge=Edge[channel['edge']])) - - elif self.settings['NIDAQ_type'] == ChannelType.COUNTER_OUTPUT.name: # counter output - source = ChannelType.COUNTER_OUTPUT - for channel in self.settings.child('counter_settings', 'co_channels').children(): - channels.append(COChannel(name=channel.opts['title'], - source=source, edge=Edge[channel['edge']])) - - elif self.settings['NIDAQ_type'] == ChannelType.DIGITAL_INPUT.name: # digital input - source = ChannelType.DIGITAL_INPUT - for channel in self.settings.child('di_channels').children(): - channels.append(DIChannel(name=channel.opts['title'], - source=source)) - - elif self.settings['NIDAQ_type'] == ChannelType.DIGITAL_OUTPUT.name: # Digital output - source = ChannelType.DIGITAL_OUTPUT + elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(3).name: # Digital output for channel in self.settings.child('do_channels').children(): channels.append(DOChannel(name=channel.opts['title'], - source=source)) - - channels = [ch for ch in channels if self.settings.child("devices").value() in ch.name] + source='Digital_Output')) + elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(4).name: # digital input + for channel in self.settings.child('di_channels').children(): + channels.append(DIChannel(name=channel.opts['title'], + source='Digital_Input')) return channels def stop(self): @@ -484,7 +453,410 @@ def stop(self): if not not self.timer: self.timer.stop() QtWidgets.QApplication.processEvents() - self.controller.stop() + DAQmx.stop(self) + + +class DAQ_NIDAQmx_Viewer(DAQ_Viewer_base, DAQ_NIDAQmx_base): + """ + ==================== ======================== + **Attributes** **Type** + *data_grabed_signal* instance of Signal + *params* dictionnary list + *task* + ==================== ======================== + + See Also + -------- + refresh_hardware + """ + + live_mode_available = True + params = viewer_params + DAQ_NIDAQmx_base.params + + def __init__(self, parent=None, params_state=None, control_type="0D"): + DAQ_Viewer_base.__init__(self, parent, params_state) #defines settings attribute and various other methods + DAQ_NIDAQmx_base.__init__(self) + + self.live = False + self.control_type = control_type # could be "0D", "1D" or "Actuator" + if self.control_type == "0D": + self.settings.child('NIDAQ_type').setLimits(['Analog_Input', 'Counter', 'Digital_Input']) # analog input and counter + elif self.control_type == "1D": + self.settings.child('NIDAQ_type').setLimits(['Analog_Input']) + elif self.control_type == "Actuator": + self.settings.child('NIDAQ_type').setLimits(['Analog_Output']) + + self.settings.child('ao_channels').hide() + + #timer used for the counter + self.timer = QtCore.QTimer() + self.timer.setSingleShot(True) + self.timer.timeout.connect(self.counter_done) + + def stop(self): + try: + self.controller['ai'].task.StopTask() + except: + pass + ############################## + + return '' + + def commit_settings(self, param): + """ + Activate the parameters changes in the hardware. + + =============== ================================ =========================== + **Parameters** **Type** **Description** + *param* instance of pyqtgraph.parameter the parameter to activate + =============== ================================ =========================== + + See Also + -------- + update_NIDAQ_channels, update_task, DAQ_NIDAQ_source, refresh_hardware + """ + + if param.parent() is not None: + if param.parent().name() == 'ai_channels': + device = param.opts['title'].split('/')[0] + self.settings.child('clock_settings', 'frequency').setOpts(max=self.getAIMaxRate(device)) + + ranges = self.getAIVoltageRange(device) + param.child('voltage_settings', 'volt_min').setOpts(limits=[r[0] for r in ranges]) + param.child('voltage_settings', 'volt_max').setOpts(limits=[r[1] for r in ranges]) + + DAQ_NIDAQmx_base.commit_settings(self, param) + + def ini_detector(self, controller=None): + """ + Initialisation procedure of the detector. + + See Also + -------- + daq_utils.ThreadCommand + """ + self.status.update(edict(initialized=False, info="", x_axis=None, y_axis=None, controller=None)) + try: + if self.settings['controller_status'] == "Slave": + if controller is None: + raise Exception('no controller has been defined externally while this detector is a slave one') + else: + self.controller = 'A Nidaqmx task' + else: + self.update_task() + + #actions to perform in order to set properly the settings tree options + self.commit_settings(self.settings.child('NIDAQ_type')) + + self.status.info = "Plugin Initialized" + self.status.initialized = True + self.status.controller = controller + return self.status + + except Exception as e: + self.emit_status(ThreadCommand('Update_Status', [str(e), 'log'])) + self.status.info = str(e) + self.status.initialized = False + return self.status + + def grab_data(self, Naverage=1, **kwargs): + """ + | grab the current values with NIDAQ profile procedure. + | + | Send the data_grabed_signal once done. + + =============== ======== =============================================== + **Parameters** **Type** **Description** + *Naverage* int Number of values to average + =============== ======== =============================================== + + See Also + -------- + DAQ_NIDAQ_source + """ + update = False + if 'live' in kwargs: + if kwargs['live'] != self.live: + update = True + self.live = kwargs['live'] + if update: + self.update_task() + + if self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(0).name: #analog input + if self.c_callback is None: + self.register_callback(self.emit_data) + elif self.settings['NIDAQ_type'] == DAQ_NIDAQ_source(1).name: #counter input + self.timer.start(self.settings['counter_settings', 'counting_time']) + self.waitTaskDone() + self.start() + + def emit_data(self, taskhandle, status, callbackdata): + channels_name = [ch.name for ch in self.channels] + + data_tot = [] + data = self.readAnalog(len(self.channels), self.clock_settings) + N = self.clock_settings.Nsamples + if self.control_type == "0D": + for ind in range(len(self.channels)): + data_tot.append(np.array([np.mean(data[ind*N:(ind+1)*N])])) + self.data_grabed_signal.emit([DataFromPlugins(name='NI AI', data=data_tot, dim='Data0D', + labels=channels_name)]) + else: + for ind in range(len(self.channels)): + data_tot.append(data[ind*N:(ind+1)*N]) + self.data_grabed_signal.emit([ + DataFromPlugins(name='NI AI', data=data_tot, dim='Data1D', + labels=channels_name, + axes=[Axis(data=np.linspace(0, N / self.clock_settings.frequency, N), + index=0)])]) + return 0 #mandatory for the PyDAQmx callback + + def counter_done(self): + channels_name = [ch.name for ch in self.channels] + data_counter = self.readCounter(len(self.channels), + self.settings['counter_settings', 'counting_time'] * 1e-3) + self.data_grabed_signal.emit([DataFromPlugins(name='NI Counter', data=[data_counter / 1e-3], dim='Data0D', + labels=channels_name,)]) + #y_axis=Axis(label='Count Number', units='1/s'))]) + self.task.StopTask() + + +class DAQ_NIDAQmx_Actuator(DAQ_Move_base, DAQ_NIDAQmx_base): + """ + Wrapper object to access the Mock fonctionnalities, similar wrapper for all controllers. + + =============== ============== + **Attributes** **Type** + *params* dictionnary + =============== ============== + """ + _controller_units = 'Volts' + is_multiaxes = False # set to True if this plugin is controlled for a multiaxis controller (with a unique communication link) + stage_names = [] # "list of strings of the multiaxes + + params = DAQ_NIDAQmx_base.params +[ + # elements to be added here as dicts in order to control your custom stage + ############ + {'title': 'MultiAxes:', 'name': 'multiaxes', 'type': 'group', 'visible': is_multiaxes, + 'children': [ + {'title': 'is Multiaxes:', 'name': 'ismultiaxes', 'type': 'bool', 'value': is_multiaxes, + 'default': False}, + {'title': 'Status:', 'name': 'multi_status', 'type': 'list', 'value': 'Master', + 'limits': ['Master', 'Slave']}, + {'title': 'Axis:', 'name': 'axis', 'type': 'list', 'limits': stage_names}, + + ]}] + actuator_params + + def __init__(self, parent=None, params_state=None, control_type="Actuator"): + DAQ_Move_base.__init__(self, parent, params_state) # defines settings attribute and various other methods + DAQ_NIDAQmx_base.__init__(self) + + self.control_type = "Actuator" # could be "0D", "1D" or "Actuator" + self.settings.child('NIDAQ_type').setLimits(['Analog_Output', 'Digital_Output']) + + self.settings.child('clock_settings', 'Nsamples').setValue(1) + + def get_actuator_value(self) -> DataActuator: + """Get the current position from the hardware with scaling conversion. + + Returns + ------- + float: The position obtained after scaling conversion. + """ + + pos = self.target_position + ## + + pos = self.get_position_with_scaling(pos) + self.emit_status(ThreadCommand('check_position', [pos])) + return pos + + + def commit_settings(self, param): + """ + | Activate any parameter changes on the PI_GCS2 hardware. + | + | Called after a param_tree_changed signal from DAQ_Move_main. + + """ + + DAQ_NIDAQmx_base.commit_settings(self, param) + if param.name() == 'waveform': + if param.value() == 'DC': + self.settings.child('ao_settings', 'cont_param').setValue('offset') + self.settings.child('ao_settings', 'cont_param').show(not param.value() == 'DC') + self.settings.child('ao_settings', 'waveform_settings').show(not param.value() == 'DC') + + if param.parent() is not None: + if param.parent().name() == 'ao_channels': + device = param.opts['title'].split('/')[0] + self.settings.child('clock_settings', 'frequency').setOpts(max=self.getAOMaxRate(device)) + + ranges = self.getAOVoltageRange(device) + param.child('voltage_settings', 'volt_min').setOpts(limits=[r[0] for r in ranges]) + param.child('voltage_settings', 'volt_max').setOpts(limits=[r[1] for r in ranges]) + + def ini_stage(self, controller=None): + """Actuator communication initialization + + Parameters + ---------- + controller: (object) custom object of a PyMoDAQ plugin (Slave case). None if only one actuator by controller (Master case) + + Returns + ------- + self.status (edict): with initialization status: three fields: + * info (str) + * controller (object) initialized controller + *initialized: (bool): False if initialization failed otherwise True + """ + + try: + # initialize the stage and its controller status + # controller is an object that may be passed to other instances of DAQ_Move_Mock in case + # of one controller controlling multiactuators (or detector) + + self.status.update(edict(info="", controller=None, initialized=False)) + + # check whether this stage is controlled by a multiaxe controller (to be defined for each plugin) + # if multiaxes then init the controller here if Master state otherwise use external controller + if self.settings['multiaxes', 'ismultiaxes'] and self.settings['multiaxes', + 'multi_status'] == "Slave": + if controller is None: + raise Exception('no controller has been defined externally while this axe is a slave one') + else: + self.controller = controller + else: + self.controller = 'A Nidaqmx task' + self.update_task() + + # actions to perform in order to set properly the settings tree options + self.commit_settings(self.settings.child('NIDAQ_type')) + + self.status.info = "Plugin Initialized" + self.status.controller = self.controller + self.status.initialized = True + return self.status + + except Exception as e: + self.emit_status(ThreadCommand('Update_Status', [getLineInfo() + str(e), 'log'])) + self.status.info = getLineInfo() + str(e) + self.status.initialized = False + return self.status + + def calulate_waveform(self, value): + waveform = self.settings['ao_settings', 'waveform'] + if waveform == 'DC': + values = np.array([value]) + else: + Nsamples = self.settings['clock_settings', 'Nsamples'] + freq = self.settings['clock_settings', 'frequency'] + time = np.linspace(0, Nsamples / freq, Nsamples, endpoint=False) + + freq0 = self.settings['ao_settings', 'waveform_settings', 'frequency'] + amp = self.settings['ao_settings', 'waveform_settings', 'amplitude'] + offset = self.settings['ao_settings', 'waveform_settings', 'offset'] + if waveform == 'Sinus': + values = offset + amp * np.sin(2*np.pi*freq0*time) + elif waveform == 'Ramp': + values = offset + amp * np.linspace(0, 1, Nsamples) + + return values + + + def move_Abs(self, position): + """ Move the actuator to the absolute target defined by position + + Parameters + ---------- + position: (flaot) value of the absolute target positioning + """ + + position = self.check_bound(position) # if user checked bounds, the defined bounds are applied here + position = self.set_position_with_scaling(position) # apply scaling if the user specified one + if self.settings['NIDAQ_type'] == 'Analog_Output': + self.settings.child('ao_settings', 'waveform_settings', + self.settings['ao_settings', 'cont_param']).setValue(position) + values = self.calulate_waveform(position) + self.target_position = position + + self.stop() + + if len(values) == 1: + self.writeAnalog(len(values), 1, values, autostart=True) + self.current_position = self.check_position() + self.move_done() + else: + if self.c_callback is None: + self.register_callback(self.move_done_callback) + self.writeAnalog(len(values), 1, values, autostart=False) + self.task.StartTask() + elif self.settings['NIDAQ_type'] == 'Digital_Output': + self.writeDigital(1, np.array([position], dtype=np.uint8), autostart=True) + + def move_done_callback(self, taskhandle, status, callbackdata): + self.current_position = self.check_position() + QtWidgets.QApplication.processEvents() + self.move_done() + self.task.StopTask() + return 0 + def move_Rel(self, position): + """ Move the actuator to the relative target actuator value defined by position + Parameters + ---------- + position: (flaot) value of the relative target positioning + """ + + position = self.check_bound(self.current_position + position) - self.current_position + self.target_position = position + self.current_position + if self.settings['NIDAQ_type'] == 'Analog_Output': + self.settings.child('ao_settings', 'waveform_settings', + self.settings['ao_settings', 'cont_param']).setValue(self.target_position) + + values = self.calulate_waveform(self.target_position) + + self.stop() + + if len(values) == 1: + self.writeAnalog(len(values), 1, values, autostart=True) + self.current_position = self.check_position() + self.move_done() + else: + if self.c_callback is None: + self.register_callback(self.move_done_callback) + self.writeAnalog(len(values), 1, values, autostart=False) + self.task.StartTask() + elif self.settings['NIDAQ_type'] == 'Digital_Output': + self.writeDigital(1, np.array([self.target_position], dtype=np.uint8), autostart=True) + + def move_Home(self): + """ + Send the update status thread command. + See Also + -------- + daq_utils.ThreadCommand + """ + + self.stop() + + if self.c_callback is None: + self.register_callback(self.move_done_callback) + self.writeAnalog(1, 1, np.array([0.])) + self.task.StartTask() + + def stop_motion(self): + """ + Call the specific move_done function (depending on the hardware). + + See Also + -------- + move_done + """ + ## TODO for your custom plugin + self.stop() + self.emit_status(ThreadCommand('Update_Status', ['Some info you want to log'])) + self.move_done() # to let the interface know the actuator stopped + ############################## \ No newline at end of file From 056e4c828efcc52b0110efccf5bcd8109d14a48b Mon Sep 17 00:00:00 2001 From: Sebastien Date: Mon, 27 Jan 2025 10:54:00 +0100 Subject: [PATCH 54/57] NIDAQmx_Move not implemented yet --- .../daq_move_plugins/daq_move_DAQmx.py | 2 +- .../national_instruments/NIDAQmx_Move.py | 246 ------------------ 2 files changed, 1 insertion(+), 247 deletions(-) delete mode 100644 src/pymodaq_plugins_daqmx/hardware/national_instruments/NIDAQmx_Move.py diff --git a/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_DAQmx.py b/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_DAQmx.py index f627b51..0fb267d 100644 --- a/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_DAQmx.py +++ b/src/pymodaq_plugins_daqmx/daq_move_plugins/daq_move_DAQmx.py @@ -1,4 +1,4 @@ -from ..hardware.national_instruments.NIDAQmx_Move import DAQ_NIDAQmx_Actuator +from ..hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_Actuator class DAQ_Move_DAQmx(DAQ_NIDAQmx_Actuator): diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/NIDAQmx_Move.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/NIDAQmx_Move.py deleted file mode 100644 index 3ad598d..0000000 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/NIDAQmx_Move.py +++ /dev/null @@ -1,246 +0,0 @@ -import numpy as np -from easydict import EasyDict as edict -from qtpy import QtWidgets -from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, comon_parameters as actuator_params -from pymodaq_plugins_daqmx.hardware.national_instruments.NIDAQmx_base import DAQ_NIDAQmx_base -from pymodaq.utils.daq_utils import ThreadCommand, getLineInfo -from pymodaq.utils.data import DataActuator -from pymodaq.utils.logger import set_logger, get_module_name -logger = set_logger(get_module_name(__file__)) - - -class DAQ_NIDAQmx_Actuator(DAQ_Move_base, DAQ_NIDAQmx_base): - """ - Wrapper object to access the Mock fonctionnalities, similar wrapper for all controllers. - - =============== ============== - **Attributes** **Type** - *params* dictionnary - =============== ============== - """ - _controller_units = 'Volts' - is_multiaxes = False # set to True if this plugin is controlled for a multiaxis controller (with a unique communication link) - stage_names = [] # "list of strings of the multiaxes - - params = DAQ_NIDAQmx_base.params + [ - # elements to be added here as dicts in order to control your custom stage - ############ - {'title': 'MultiAxes:', 'name': 'multiaxes', 'type': 'group', 'visible': is_multiaxes, - 'children': [ - {'title': 'is Multiaxes:', 'name': 'ismultiaxes', 'type': 'bool', 'value': is_multiaxes, - 'default': False}, - {'title': 'Status:', 'name': 'multi_status', 'type': 'list', 'value': 'Master', - 'limits': ['Master', 'Slave']}, - {'title': 'Axis:', 'name': 'axis', 'type': 'list', 'limits': stage_names}, - ]}] + actuator_params() - - def __init__(self, parent=None, params_state=None, control_type="Actuator"): - DAQ_Move_base.__init__(self, parent, params_state) # defines settings attribute and various other methods - DAQ_NIDAQmx_base.__init__(self) - - self.control_type = "Actuator" # could be "0D", "1D" or "Actuator" - self.settings.child('NIDAQ_type').setLimits(['Analog_Output', 'Digital_Output']) - - self.settings.child('clock_settings', 'Nsamples').setValue(1) - - def get_actuator_value(self) -> DataActuator: - """Get the current position from the hardware with scaling conversion. - - Returns - ------- - float: The position obtained after scaling conversion. - """ - - pos = self.target_position - ## - - pos = self.get_position_with_scaling(pos) - self.emit_status(ThreadCommand('check_position', [pos])) - return pos - - def commit_settings(self, param): - """ - | Activate any parameter changes on the PI_GCS2 hardware. - | - | Called after a param_tree_changed signal from DAQ_Move_main. - - """ - - DAQ_NIDAQmx_base.commit_settings(self, param) - if param.name() == 'waveform': - if param.value() == 'DC': - self.settings.child('ao_settings', 'cont_param').setValue('offset') - self.settings.child('ao_settings', 'cont_param').show(not param.value() == 'DC') - self.settings.child('ao_settings', 'waveform_settings').show(not param.value() == 'DC') - - if param.parent() is not None: - if param.parent().name() == 'ao_channels': - device = param.opts['title'].split('/')[0] - self.settings.child('clock_settings', 'frequency').setOpts(max=self.getAOMaxRate(device)) - - ranges = self.getAOVoltageRange(device) - param.child('voltage_settings', 'volt_min').setOpts(limits=[r[0] for r in ranges]) - param.child('voltage_settings', 'volt_max').setOpts(limits=[r[1] for r in ranges]) - - def ini_stage(self, controller=None): - """Actuator communication initialization - - Parameters - ---------- - controller: (object) custom object of a PyMoDAQ plugin (Slave case). None if only one actuator by controller (Master case) - - Returns - ------- - self.status (easydict): with initialization status: three fields: - * info (str) - * controller (object) initialized controller - *initialized: (bool): False if initialization failed otherwise True - """ - - try: - # initialize the stage and its controller status - # controller is an object that may be passed to other instances of DAQ_Move_Mock in case - # of one controller controlling multiactuators (or detector) - - self.status.update(edict(info="", controller=None, initialized=False)) - - # check whether this stage is controlled by a multiaxe controller (to be defined for each plugin) - # if multiaxes then init the controller here if Master state otherwise use external controller - if self.settings['multiaxes', 'ismultiaxes'] and self.settings['multiaxes', - 'multi_status'] == "Slave": - if controller is None: - raise Exception('no controller has been defined externally while this axe is a slave one') - else: - self.controller = controller - else: - self.controller = 'A Nidaqmx task' - self.update_task() - - # actions to perform in order to set properly the settings tree options - self.commit_settings(self.settings.child('NIDAQ_type')) - - self.status.info = "Plugin Initialized" - self.status.controller = self.controller - self.status.initialized = True - return self.status - - except Exception as e: - self.emit_status(ThreadCommand('Update_Status', [getLineInfo() + str(e), 'log'])) - self.status.info = getLineInfo() + str(e) - self.status.initialized = False - return self.status - - def calulate_waveform(self, value): - waveform = self.settings['ao_settings', 'waveform'] - if waveform == 'DC': - values = np.array([value]) - else: - Nsamples = self.settings['clock_settings', 'Nsamples'] - freq = self.settings['clock_settings', 'frequency'] - time = np.linspace(0, Nsamples / freq, Nsamples, endpoint=False) - - freq0 = self.settings['ao_settings', 'waveform_settings', 'frequency'] - amp = self.settings['ao_settings', 'waveform_settings', 'amplitude'] - offset = self.settings['ao_settings', 'waveform_settings', 'offset'] - if waveform == 'Sinus': - values = offset + amp * np.sin(2 * np.pi * freq0 * time) - elif waveform == 'Ramp': - values = offset + amp * np.linspace(0, 1, Nsamples) - - return values - - def move_abs(self, position): - """ Move the actuator to the absolute target defined by position - - Parameters - ---------- - position: (flaot) value of the absolute target positioning - """ - - position = self.check_bound(position) # if user checked bounds, the defined bounds are applied here - position = self.set_position_with_scaling(position) # apply scaling if the user specified one - if self.settings['NIDAQ_type'] == 'Analog_Output': - self.settings.child('ao_settings', 'waveform_settings', - self.settings['ao_settings', 'cont_param']).setValue(position) - values = self.calulate_waveform(position) - self.target_position = position - - self.stop() - - if len(values) == 1: - self.writeAnalog(len(values), 1, values, autostart=True) - self.current_position = self.check_position() - self.move_done() - else: - if self.c_callback is None: - self.register_callback(self.move_done_callback) - self.writeAnalog(len(values), 1, values, autostart=False) - self.task.StartTask() - elif self.settings['NIDAQ_type'] == 'Digital_Output': - self.writeDigital(1, np.array([position], dtype=np.uint8), autostart=True) - - def move_done_callback(self, taskhandle, status, callbackdata): - self.current_position = self.check_position() - QtWidgets.QApplication.processEvents() - self.move_done() - self.task.StopTask() - return 0 - - def move_rel(self, position): - """ Move the actuator to the relative target actuator value defined by position - - Parameters - ---------- - position: (flaot) value of the relative target positioning - """ - - position = self.check_bound(self.current_position + position) - self.current_position - self.target_position = position + self.current_position - if self.settings['NIDAQ_type'] == 'Analog_Output': - self.settings.child('ao_settings', 'waveform_settings', - self.settings['ao_settings', 'cont_param']).setValue(self.target_position) - - values = self.calulate_waveform(self.target_position) - - self.stop() - - if len(values) == 1: - self.writeAnalog(len(values), 1, values, autostart=True) - self.current_position = self.check_position() - self.move_done() - else: - if self.c_callback is None: - self.register_callback(self.move_done_callback) - self.writeAnalog(len(values), 1, values, autostart=False) - self.task.StartTask() - elif self.settings['NIDAQ_type'] == 'Digital_Output': - self.writeDigital(1, np.array([self.target_position], dtype=np.uint8), autostart=True) - - def move_home(self): - """ - Send the update status thread command. - See Also - -------- - daq_utils.ThreadCommand - """ - - self.stop() - - if self.c_callback is None: - self.register_callback(self.move_done_callback) - self.writeAnalog(1, 1, np.array([0.])) - self.task.StartTask() - - def stop_motion(self): - """ - Call the specific move_done function (depending on the hardware). - - See Also - -------- - move_done - """ - - ## TODO for your custom plugin - self.stop() - self.emit_status(ThreadCommand('Update_Status', ['Some info you want to log'])) - self.move_done() # to let the interface know the actuator stopped From 8d3cb707b99f909ee5666863f7a9295dee1ad494 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Mon, 27 Jan 2025 11:13:46 +0100 Subject: [PATCH 55/57] NIDAQmx_Move not implemented yet --- .../national_instruments/daq_NIDAQmx_Move.py | 246 ------------------ 1 file changed, 246 deletions(-) delete mode 100644 src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Move.py diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Move.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Move.py deleted file mode 100644 index 396e910..0000000 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daq_NIDAQmx_Move.py +++ /dev/null @@ -1,246 +0,0 @@ -import numpy as np -from easydict import EasyDict as edict -from qtpy import QtWidgets -from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, comon_parameters as actuator_params -from pymodaq_plugins_daqmx.hardware.national_instruments.daq_NIDAQmx import DAQ_NIDAQmx_base -from pymodaq.utils.daq_utils import ThreadCommand, getLineInfo -from pymodaq.utils.data import DataActuator -from pymodaq.utils.logger import set_logger, get_module_name -logger = set_logger(get_module_name(__file__)) - - -class DAQ_NIDAQmx_Actuator(DAQ_Move_base, DAQ_NIDAQmx_base): - """ - Wrapper object to access the Mock fonctionnalities, similar wrapper for all controllers. - - =============== ============== - **Attributes** **Type** - *params* dictionnary - =============== ============== - """ - _controller_units = 'Volts' - is_multiaxes = False # set to True if this plugin is controlled for a multiaxis controller (with a unique communication link) - stage_names = [] # "list of strings of the multiaxes - - params = DAQ_NIDAQmx_base.params + [ - # elements to be added here as dicts in order to control your custom stage - ############ - {'title': 'MultiAxes:', 'name': 'multiaxes', 'type': 'group', 'visible': is_multiaxes, - 'children': [ - {'title': 'is Multiaxes:', 'name': 'ismultiaxes', 'type': 'bool', 'value': is_multiaxes, - 'default': False}, - {'title': 'Status:', 'name': 'multi_status', 'type': 'list', 'value': 'Master', - 'limits': ['Master', 'Slave']}, - {'title': 'Axis:', 'name': 'axis', 'type': 'list', 'limits': stage_names}, - ]}] + actuator_params() - - def __init__(self, parent=None, params_state=None, control_type="Actuator"): - DAQ_Move_base.__init__(self, parent, params_state) # defines settings attribute and various other methods - DAQ_NIDAQmx_base.__init__(self) - - self.control_type = "Actuator" # could be "0D", "1D" or "Actuator" - self.settings.child('NIDAQ_type').setLimits(['Analog_Output', 'Digital_Output']) - - self.settings.child('clock_settings', 'Nsamples').setValue(1) - - def get_actuator_value(self) -> DataActuator: - """Get the current position from the hardware with scaling conversion. - - Returns - ------- - float: The position obtained after scaling conversion. - """ - - pos = self.target_position - ## - - pos = self.get_position_with_scaling(pos) - self.emit_status(ThreadCommand('check_position', [pos])) - return pos - - def commit_settings(self, param): - """ - | Activate any parameter changes on the PI_GCS2 hardware. - | - | Called after a param_tree_changed signal from DAQ_Move_main. - - """ - - DAQ_NIDAQmx_base.commit_settings(self, param) - if param.name() == 'waveform': - if param.value() == 'DC': - self.settings.child('ao_settings', 'cont_param').setValue('offset') - self.settings.child('ao_settings', 'cont_param').show(not param.value() == 'DC') - self.settings.child('ao_settings', 'waveform_settings').show(not param.value() == 'DC') - - if param.parent() is not None: - if param.parent().name() == 'ao_channels': - device = param.opts['title'].split('/')[0] - self.settings.child('clock_settings', 'frequency').setOpts(max=self.getAOMaxRate(device)) - - ranges = self.getAOVoltageRange(device) - param.child('voltage_settings', 'volt_min').setOpts(limits=[r[0] for r in ranges]) - param.child('voltage_settings', 'volt_max').setOpts(limits=[r[1] for r in ranges]) - - def ini_stage(self, controller=None): - """Actuator communication initialization - - Parameters - ---------- - controller: (object) custom object of a PyMoDAQ plugin (Slave case). None if only one actuator by controller (Master case) - - Returns - ------- - self.status (easydict): with initialization status: three fields: - * info (str) - * controller (object) initialized controller - *initialized: (bool): False if initialization failed otherwise True - """ - - try: - # initialize the stage and its controller status - # controller is an object that may be passed to other instances of DAQ_Move_Mock in case - # of one controller controlling multiactuators (or detector) - - self.status.update(edict(info="", controller=None, initialized=False)) - - # check whether this stage is controlled by a multiaxe controller (to be defined for each plugin) - # if multiaxes then init the controller here if Master state otherwise use external controller - if self.settings['multiaxes', 'ismultiaxes'] and self.settings['multiaxes', - 'multi_status'] == "Slave": - if controller is None: - raise Exception('no controller has been defined externally while this axe is a slave one') - else: - self.controller = controller - else: - self.controller = 'A Nidaqmx task' - self.update_task() - - # actions to perform in order to set properly the settings tree options - self.commit_settings(self.settings.child('NIDAQ_type')) - - self.status.info = "Plugin Initialized" - self.status.controller = self.controller - self.status.initialized = True - return self.status - - except Exception as e: - self.emit_status(ThreadCommand('Update_Status', [getLineInfo() + str(e), 'log'])) - self.status.info = getLineInfo() + str(e) - self.status.initialized = False - return self.status - - def calulate_waveform(self, value): - waveform = self.settings['ao_settings', 'waveform'] - if waveform == 'DC': - values = np.array([value]) - else: - Nsamples = self.settings['clock_settings', 'Nsamples'] - freq = self.settings['clock_settings', 'frequency'] - time = np.linspace(0, Nsamples / freq, Nsamples, endpoint=False) - - freq0 = self.settings['ao_settings', 'waveform_settings', 'frequency'] - amp = self.settings['ao_settings', 'waveform_settings', 'amplitude'] - offset = self.settings['ao_settings', 'waveform_settings', 'offset'] - if waveform == 'Sinus': - values = offset + amp * np.sin(2 * np.pi * freq0 * time) - elif waveform == 'Ramp': - values = offset + amp * np.linspace(0, 1, Nsamples) - - return values - - def move_abs(self, position): - """ Move the actuator to the absolute target defined by position - - Parameters - ---------- - position: (float) value of the absolute target positioning - """ - - position = self.check_bound(position) # if user checked bounds, the defined bounds are applied here - position = self.set_position_with_scaling(position) # apply scaling if the user specified one - if self.settings['NIDAQ_type'] == 'Analog_Output': - self.settings.child('ao_settings', 'waveform_settings', - self.settings['ao_settings', 'cont_param']).setValue(position) - values = self.calulate_waveform(position) - self.target_position = position - - self.stop() - - if len(values) == 1: - self.writeAnalog(len(values), 1, values, autostart=True) - self.current_position = self.check_position() - self.move_done() - else: - if self.c_callback is None: - self.register_callback(self.move_done_callback) - self.writeAnalog(len(values), 1, values, autostart=False) - self.task.StartTask() - elif self.settings['NIDAQ_type'] == 'Digital_Output': - self.writeDigital(1, np.array([position], dtype=np.uint8), autostart=True) - - def move_done_callback(self, taskhandle, status, callbackdata): - self.current_position = self.check_position() - QtWidgets.QApplication.processEvents() - self.move_done() - self.task.StopTask() - return 0 - - def move_rel(self, position): - """ Move the actuator to the relative target actuator value defined by position - - Parameters - ---------- - position: (float) value of the relative target positioning - """ - - position = self.check_bound(self.current_position + position) - self.current_position - self.target_position = position + self.current_position - if self.settings['NIDAQ_type'] == 'Analog_Output': - self.settings.child('ao_settings', 'waveform_settings', - self.settings['ao_settings', 'cont_param']).setValue(self.target_position) - - values = self.calulate_waveform(self.target_position) - - self.stop() - - if len(values) == 1: - self.writeAnalog(len(values), 1, values, autostart=True) - self.current_position = self.check_position() - self.move_done() - else: - if self.c_callback is None: - self.register_callback(self.move_done_callback) - self.writeAnalog(len(values), 1, values, autostart=False) - self.task.StartTask() - elif self.settings['NIDAQ_type'] == 'Digital_Output': - self.writeDigital(1, np.array([self.target_position], dtype=np.uint8), autostart=True) - - def move_home(self): - """ - Send the update status thread command. - See Also - -------- - daq_utils.ThreadCommand - """ - - self.stop() - - if self.c_callback is None: - self.register_callback(self.move_done_callback) - self.writeAnalog(1, 1, np.array([0.])) - self.task.StartTask() - - def stop_motion(self): - """ - Call the specific move_done function (depending on the hardware). - - See Also - -------- - move_done - """ - - ## TODO for your custom plugin - self.stop() - self.emit_status(ThreadCommand('Update_Status', ['Some info you want to log'])) - self.move_done() # to let the interface know the actuator stopped From 47d6e13c6f502b13f957663dab925b15cd2cbd88 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Mon, 27 Jan 2025 16:25:40 +0100 Subject: [PATCH 56/57] UsageTypeCI added for future counter implementation --- .../hardware/national_instruments/daqmxni.py | 60 +++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index 039fb6e..2955c55 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -408,22 +408,31 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti if err_code: status = self.DAQmxGetErrorString(err_code) raise IOError(status) - elif channel.source == 'Counter': # counter + elif channel.source == ChannelType.ANALOG_OUTPUT: # Analog_Output + try: + if channel.analog_type == UsageTypeAI.VOLTAGE: + self._task.ao_channels.add_ao_voltage_chan(channel.name, "", + channel.value_min, + channel.value_max, + VoltageUnits.VOLTS, None) + + elif channel.analog_type == UsageTypeAI.CURRENT: + self._task.ao_channels.add_ao_current_chan(channel.name, "", + channel.value_min, + channel.value_max, + VoltageUnits.VOLTS, None) + except DaqError as e: + err_code = e.error_code + if not not err_code: + status = self.DAQmxGetErrorString(err_code) + raise IOError(status) + elif channel.source == ChannelType.COUNTER_INPUT: # counter input try: - if channel.counter_type == "Edge Counter": + if channel.counter_type == UsageTypeCI.COUNT_EDGES: self._task.ci_channels.add_ci_count_edges_chan(channel.name, "", channel.edge, 0, CountDirection.COUNT_UP) - - elif channel.counter_type == "Clock Output": - self._task.co_channels.add_co_pulse_chan_freq(channel.name, "clock task", - FrequencyUnits.HZ, - Level.LOW, - 0, - channel.clock_frequency, - 0.5) - - elif channel.counter_type == "SemiPeriod Input": + elif channel.counter_type == UsageTypeCI.PULSE_WIDTH_DIGITAL_SEMI_PERIOD: self._task.ci_channels.add_ci_semi_period_chan(channel.name, "counter task", 0, # expected min channel.value_max, # expected max @@ -439,21 +448,23 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti if not not err_code: status = self.DAQmxGetErrorString(err_code) raise IOError(status) - elif channel.source == ChannelType.ANALOG_OUTPUT: # Analog_Output + elif channel.source == ChannelType.COUNTER_OUTPUT: # counter output try: - if channel.analog_type == UsageTypeAI.VOLTAGE: - self._task.ao_channels.add_ao_voltage_chan(channel.name, "", - channel.value_min, - channel.value_max, - VoltageUnits.VOLTS, None) + if channel.counter_type == UsageTypeCO.PULSE_FREQUENCY: + self._task.co_channels.add_co_pulse_chan_freq(channel.name, "clock task", + FrequencyUnits.HZ, + Level.LOW, + 0, + channel.clock_frequency, + 0.5) - elif channel.analog_type == UsageTypeAI.CURRENT: - self._task.ao_channels.add_ao_current_chan(channel.name, "", - channel.value_min, - channel.value_max, - VoltageUnits.VOLTS, None) except DaqError as e: err_code = e.error_code + + if not not err_code: + status = self.DAQmxGetErrorString(err_code) + raise IOError(status) + if not not err_code: status = self.DAQmxGetErrorString(err_code) raise IOError(status) @@ -503,7 +514,7 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti for channel in channels: if not trigger_settings.enable: - if channel.source == 'Counter': + if channel.source == ChannelType.COUNTER_INPUT: pass # Maybe here adding the configuration fastCTr0 with Ctr1 etc...? else: pass @@ -529,7 +540,6 @@ def register_callback(self, callback, event='done', nsamples=1): if event == 'done': self._task.register_done_event(callback) - # NOT SURE HERE elif event == 'sample': self._task.register_every_n_samples_acquired_into_buffer_event(1, callback) elif event == 'Nsamples': From 662d2f4a3654a51688768a2786aa92644168d2d7 Mon Sep 17 00:00:00 2001 From: Sebastien Date: Mon, 27 Jan 2025 16:47:49 +0100 Subject: [PATCH 57/57] [completion] --- .../hardware/national_instruments/daqmxni.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py index 2955c55..cf7ebe8 100644 --- a/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py +++ b/src/pymodaq_plugins_daqmx/hardware/national_instruments/daqmxni.py @@ -410,13 +410,13 @@ def update_task(self, channels=[], clock_settings=ClockSettings(), trigger_setti raise IOError(status) elif channel.source == ChannelType.ANALOG_OUTPUT: # Analog_Output try: - if channel.analog_type == UsageTypeAI.VOLTAGE: + if channel.analog_type == UsageTypeAO.VOLTAGE: self._task.ao_channels.add_ao_voltage_chan(channel.name, "", channel.value_min, channel.value_max, VoltageUnits.VOLTS, None) - elif channel.analog_type == UsageTypeAI.CURRENT: + elif channel.analog_type == UsageTypeAO.CURRENT: self._task.ao_channels.add_ao_current_chan(channel.name, "", channel.value_min, channel.value_max,