DynExp
Highly flexible laboratory automation for dynamically changing experiments.
HardwareAdapterNIDAQ.h
Go to the documentation of this file.
1 // This file is part of DynExp.
2 
9 #pragma once
10 
11 #include "stdafx.h"
12 #include "HardwareAdapter.h"
13 #include "../MetaInstruments/DataStreamInstrument.h"
14 
16 {
17  #include "../include/NIDAQ/NIDAQmx.h"
18 }
19 
20 namespace DynExpHardware
21 {
23 
25  {
26  public:
27  NIDAQException(std::string Description, const int ErrorCode, Util::ErrorType Type = Util::ErrorType::Error,
28  const std::source_location Location = std::source_location::current()) noexcept
29  : Exception(std::move(Description), Type, ErrorCode, Location)
30  {}
31  };
32 
34  {
35  public:
37 
38  private:
39  static constexpr UseOnlyOnBrdMemType DefaultUseOnlyOnBrdMem = UseOnlyOnBrdMemType::UseMemoryBuffer;
40 
41  public:
43 
45  : UseOnlyOnBrdMem{ Owner, UseOnlyOnBrdMemTypeStrList(), "UseOnlyOnBrdMem", "Onboard memory usage",
46  "Determines whether samples are written using a buffered transfer or a direct transfer to the device's onboard memory.", true, DefaultUseOnlyOnBrdMem }
47  {}
48 
49  void DisableUserEditable();
50 
55  };
56 
58  {
59  public:
62 
65 
68  virtual ~NIDAQHardwareAdapterParams() = default;
69 
70  virtual const char* GetParamClassTag() const noexcept override { return "NIDAQHardwareAdapterParams"; }
71 
73  "If timing is configured for multiple channels (stream size > 1 sample), they have to be combined into a number of NI-DAQmx tasks limited by the amount of the respective device's clocks.",
74  true, ChannelModeType::TaskPerChannel };
75 
78 
80  "Trigger assigned to this channel to start sample generation/acquisition", true, TriggerModeType::Disabled };
81  DynExp::ParamsBase::Param<ParamsConfigDialog::TextType> TriggerChannel = { *this, "TriggerChannelName", "Trigger channel name",
82  "Path of the trigger channel to be used for triggering (ignored if triggering is disabled)", true, "/Dev1/PFI0" };
83 
84  private:
87  };
88 
90  {
91  public:
94 
96  virtual ~NIDAQHardwareAdapterConfigurator() = default;
97 
98  private:
99  virtual DynExp::ParamsBasePtrType MakeParams(DynExp::ItemIDType ID, const DynExp::DynExpCore& Core) const override { return DynExp::MakeParams<NIDAQHardwareAdapterConfigurator>(ID, Core); }
100  };
101 
102  class NIDAQTask
103  {
104  friend class NIDAQHardwareAdapter;
105 
106  public:
107  using ChannelHandleType = size_t;
108  using AnalogValueType = double;
109  using DigitalValueType = uint8_t;
110 
113 
114  NIDAQTask(ChannelType Type, NIDAQSyms::TaskHandle NITask, NIDAQHardwareAdapterParams::ChannelModeType ChannelMode = NIDAQHardwareAdapterParams::ChannelModeType::TaskPerChannel);
115  ~NIDAQTask();
116 
117  auto GetType() const noexcept { return Type; }
118  bool IsCombined() const noexcept { return ChannelMode == NIDAQHardwareAdapterParams::ChannelModeType::CombineChannels; }
119  auto GetTimeout() const noexcept { return Timeout; }
120  auto GetSamplingRate() const noexcept { return SamplingRate; }
121  auto GetSamplingMode() const noexcept { return SamplingMode; }
122  auto GetNumSamples() const noexcept { return NumSamples; }
123  bool IsMultisample() const noexcept { return NumSamples > 1; }
124  auto GetNumChannels() const noexcept { return NumChannels; }
125  auto GetBufferSizeInSamples() const noexcept { return NumSamples * NumChannels; }
126  auto GetSampleSizeInBytes() const noexcept { return (Type == ChannelType::DigialIn || Type == ChannelType::DigitalOut) ? sizeof(DigitalValueType) : sizeof(AnalogValueType); }
127  auto GetChannelIndex(ChannelHandleType ChannelHandle) const { return ChannelIndexMap.at(ChannelHandle); }
128 
129  private:
131  {
132  CircularStream(size_t BufferSize);
133 
134  void Clear();
135 
137  std::iostream Stream;
138  };
139 
140  void AddChannel(ChannelHandleType ChannelHandle, uint64_t NumSamples);
141 
143  const NIDAQSyms::TaskHandle NITask;
145 
146  double Timeout = 0; // in seconds
147  double SamplingRate = 0; // in samples per second
149  uint64_t NumSamples = 1;
150  uint32_t NumChannels = 0;
151 
152  std::map<ChannelHandleType, uint32_t> ChannelIndexMap;
153  std::vector<DigitalValueType> DigitalValues;
154  std::vector<AnalogValueType> AnalogValues;
155  std::vector<std::unique_ptr<CircularStream>> ReadStreamPerChannel;
156  };
157 
159  {
160  public:
164 
165  constexpr static auto Name() noexcept { return "NI-DAQmx"; }
166  constexpr static auto Category() noexcept { return "I/O"; }
167  static auto Enumerate();
168 
170  virtual ~NIDAQHardwareAdapter();
171 
172  virtual std::string GetName() const override { return Name(); }
173  virtual std::string GetCategory() const override { return Category(); }
174 
175  // Timeout in seconds, MinValue and MaxValue in volts.
176  ChannelHandleType InitializeDigitalInChannel(std::string_view ChannelName, double Timeout = 0) const;
177  ChannelHandleType InitializeDigitalOutChannel(std::string_view ChannelName,
178  NIDAQOutputPortParamsExtension::UseOnlyOnBrdMemType UseOnlyOnBrdMem, double Timeout = 0) const;
179  ChannelHandleType InitializeAnalogInChannel(std::string_view ChannelName, double MinValue, double MaxValue, double Timeout = 0,
180  int32_t TerminalConfig = DAQmx_Val_RSE) const;
181  ChannelHandleType InitializeAnalogOutChannel(std::string_view ChannelName, double MinValue, double MaxValue,
182  NIDAQOutputPortParamsExtension::UseOnlyOnBrdMemType UseOnlyOnBrdMem, double Timeout = 0) const;
183  bool DeregisterChannel(ChannelHandleType ChannelHandle) const;
184 
185  std::vector<NIDAQTask::DigitalValueType> ReadDigitalValues(ChannelHandleType ChannelHandle) const;
186  int32_t WriteDigitalValues(ChannelHandleType ChannelHandle, const std::vector<NIDAQTask::DigitalValueType>& Values) const;
187  std::vector<NIDAQTask::AnalogValueType> ReadAnalogValues(ChannelHandleType ChannelHandle) const;
188  int32_t WriteAnalogValues(ChannelHandleType ChannelHandle, const std::vector<NIDAQTask::AnalogValueType>& Values) const;
189 
190  void StartTask(ChannelHandleType ChannelHandle) const;
191  void StopTask(ChannelHandleType ChannelHandle) const;
192  void RestartTask(ChannelHandleType ChannelHandle) const;
193  bool HasFinishedTask(ChannelHandleType ChannelHandle) const;
194  const NIDAQTask* GetTask(ChannelHandleType ChannelHandle) const;
195 
196  private:
197  using TasksMapType = std::unordered_map<ChannelHandleType, std::shared_ptr<NIDAQTask>>;
198 
199  void ResetImpl(dispatch_tag<HardwareAdapterBase>) override final;
201 
202  void EnsureReadyStateChild() override final;
203  bool IsReadyChild() const override final;
204  bool IsConnectedChild() const noexcept override final;
205 
206  // Not thread-safe, must be called from function calling AcquireLock().
207  void CheckError(const int32_t Result, const std::source_location Location = std::source_location::current()) const;
208  void CheckReadError(NIDAQTask* Task, const int32_t Result, const std::source_location Location = std::source_location::current()) const;
209 
210  NIDAQSyms::TaskHandle CreateTaskUnsafe() const;
212  double Timeout, double SamplingRate, DynExpInstr::NumericSampleStreamParamsExtension::SamplingModeType SamplingMode) const;
213  void InitializeTriggerUnsafe(NIDAQTask* Task, NIDAQHardwareAdapterParams::TriggerModeType TriggerMode, std::string_view TriggerChannelName) const;
214  void StartTaskUnsafe(NIDAQTask* Task) const;
215  void StopTaskUnsafe(NIDAQTask* Task) const;
216  void RestartTaskUnsafe(NIDAQTask* Task) const;
217  bool HasFinishedTaskUnsafe(NIDAQTask* Task) const;
218 
219  ChannelHandleType ChannelNameToChannelHandle(std::string_view ChannelName) const;
220  bool TaskExistsUnsafe(ChannelHandleType ChannelHandle) const;
221  bool TaskExistsUnsafe(std::string_view ChannelName) const;
222  NIDAQTask* GetTaskUnsafe(ChannelHandleType ChannelHandle) const;
223  NIDAQTask* GetTaskUnsafe(std::string_view ChannelName) const;
224  ChannelHandleType InsertTaskUnsafe(std::string_view ChannelName, std::shared_ptr<NIDAQTask>&& TaskHandle) const;
225  ChannelHandleType CreateTaskIfNotExistsUnsafe(std::string_view ChannelName, NIDAQTask::ChannelType Type) const;
226  bool RemoveTaskUnsafe(ChannelHandleType ChannelHandle) const;
227 
228  // Map holding NIDAQ's tasks indexed by std::hash of NIDAQTask::ChannelName
229  // Logical const-ness: mutable, so that Instruments can (indirectly) make use of task map.
231  };
232 }
Implementation of DynExp hardware adapter objects.
NIDAQException(std::string Description, const int ErrorCode, Util::ErrorType Type=Util::ErrorType::Error, const std::source_location Location=std::source_location::current()) noexcept
virtual DynExp::ParamsBasePtrType MakeParams(DynExp::ItemIDType ID, const DynExp::DynExpCore &Core) const override
Override to make derived classes call DynExp::MakeParams with the correct configurator type derived f...
NIDAQHardwareAdapterParams(DynExp::ItemIDType ID, const DynExp::DynExpCore &Core)
virtual void ConfigureParamsImpl(dispatch_tag< NIDAQHardwareAdapterParams >)
DynExpInstr::StreamSizeParamsExtension StreamSizeParams
DynExp::ParamsBase::Param< TriggerModeType > TriggerMode
virtual const char * GetParamClassTag() const noexcept override
This function is intended to be overridden once in each derived class returning the name of the respe...
static Util::TextValueListType< ChannelModeType > ChannelModeTypeStrList()
DynExpInstr::NumericSampleStreamParamsExtension NumericSampleStreamParams
DynExp::ParamsBase::Param< ChannelModeType > ChannelMode
static Util::TextValueListType< TriggerModeType > TriggerModeTypeStrList()
void ConfigureParamsImpl(dispatch_tag< HardwareAdapterParamsBase >) override final
DynExp::ParamsBase::Param< ParamsConfigDialog::TextType > TriggerChannel
ChannelHandleType InitializeDigitalOutChannel(std::string_view ChannelName, NIDAQOutputPortParamsExtension::UseOnlyOnBrdMemType UseOnlyOnBrdMem, double Timeout=0) const
void EnsureReadyStateChild() override final
Ensures that this Object instance is ready by possibly starting its worker thread or by opening conne...
void StartTask(ChannelHandleType ChannelHandle) const
void StartTaskUnsafe(NIDAQTask *Task) const
bool DeregisterChannel(ChannelHandleType ChannelHandle) const
bool HasFinishedTask(ChannelHandleType ChannelHandle) const
void InitializeTaskTimingUnsafe(NIDAQTask *Task, double Timeout, double SamplingRate, DynExpInstr::NumericSampleStreamParamsExtension::SamplingModeType SamplingMode) const
int32_t WriteAnalogValues(ChannelHandleType ChannelHandle, const std::vector< NIDAQTask::AnalogValueType > &Values) const
ChannelHandleType InitializeAnalogInChannel(std::string_view ChannelName, double MinValue, double MaxValue, double Timeout=0, int32_t TerminalConfig=DAQmx_Val_RSE) const
bool TaskExistsUnsafe(ChannelHandleType ChannelHandle) const
void StopTaskUnsafe(NIDAQTask *Task) const
void CheckReadError(NIDAQTask *Task, const int32_t Result, const std::source_location Location=std::source_location::current()) const
std::unordered_map< ChannelHandleType, std::shared_ptr< NIDAQTask > > TasksMapType
constexpr static auto Category() noexcept
ChannelHandleType CreateTaskIfNotExistsUnsafe(std::string_view ChannelName, NIDAQTask::ChannelType Type) const
virtual std::string GetCategory() const override
Returns the category of this Object type.
void StopTask(ChannelHandleType ChannelHandle) const
bool IsConnectedChild() const noexcept override final
Determines the connection status of the hardware interface.
ChannelHandleType InsertTaskUnsafe(std::string_view ChannelName, std::shared_ptr< NIDAQTask > &&TaskHandle) const
ChannelHandleType InitializeAnalogOutChannel(std::string_view ChannelName, double MinValue, double MaxValue, NIDAQOutputPortParamsExtension::UseOnlyOnBrdMemType UseOnlyOnBrdMem, double Timeout=0) const
virtual void ResetImpl(dispatch_tag< NIDAQHardwareAdapter >)
NIDAQTask::ChannelHandleType ChannelHandleType
bool HasFinishedTaskUnsafe(NIDAQTask *Task) const
NIDAQTask * GetTaskUnsafe(ChannelHandleType ChannelHandle) const
NIDAQSyms::TaskHandle CreateTaskUnsafe() const
void RestartTaskUnsafe(NIDAQTask *Task) const
const NIDAQTask * GetTask(ChannelHandleType ChannelHandle) const
std::vector< NIDAQTask::DigitalValueType > ReadDigitalValues(ChannelHandleType ChannelHandle) const
constexpr static auto Name() noexcept
void InitializeTriggerUnsafe(NIDAQTask *Task, NIDAQHardwareAdapterParams::TriggerModeType TriggerMode, std::string_view TriggerChannelName) const
ChannelHandleType InitializeDigitalInChannel(std::string_view ChannelName, double Timeout=0) const
bool IsReadyChild() const override final
Returns wheter this Object instance is ready (e.g. it is running or connected to a hardware device) a...
void RestartTask(ChannelHandleType ChannelHandle) const
virtual std::string GetName() const override
Returns the name of this Object type.
NIDAQHardwareAdapter(const std::thread::id OwnerThreadID, DynExp::ParamsBasePtrType &&Params)
ChannelHandleType ChannelNameToChannelHandle(std::string_view ChannelName) const
void ResetImpl(dispatch_tag< HardwareAdapterBase >) override final
bool RemoveTaskUnsafe(ChannelHandleType ChannelHandle) const
int32_t WriteDigitalValues(ChannelHandleType ChannelHandle, const std::vector< NIDAQTask::DigitalValueType > &Values) const
std::vector< NIDAQTask::AnalogValueType > ReadAnalogValues(ChannelHandleType ChannelHandle) const
void CheckError(const int32_t Result, const std::source_location Location=std::source_location::current()) const
DynExp::ParamsBase::Param< UseOnlyOnBrdMemType > UseOnlyOnBrdMem
Write directly to device's onboard memory?
static constexpr UseOnlyOnBrdMemType DefaultUseOnlyOnBrdMem
static Util::TextValueListType< UseOnlyOnBrdMemType > UseOnlyOnBrdMemTypeStrList()
NIDAQOutputPortParamsExtension(DynExp::ParamsBase &Owner)
bool IsCombined() const noexcept
std::vector< AnalogValueType > AnalogValues
auto GetSamplingMode() const noexcept
bool IsMultisample() const noexcept
NIDAQTask(ChannelType Type, NIDAQSyms::TaskHandle NITask, NIDAQHardwareAdapterParams::ChannelModeType ChannelMode=NIDAQHardwareAdapterParams::ChannelModeType::TaskPerChannel)
std::map< ChannelHandleType, uint32_t > ChannelIndexMap
auto GetSamplingRate() const noexcept
auto GetNumChannels() const noexcept
auto GetTimeout() const noexcept
const NIDAQHardwareAdapterParams::ChannelModeType ChannelMode
auto GetType() const noexcept
auto GetChannelIndex(ChannelHandleType ChannelHandle) const
auto GetNumSamples() const noexcept
auto GetSampleSizeInBytes() const noexcept
void AddChannel(ChannelHandleType ChannelHandle, uint64_t NumSamples)
const NIDAQSyms::TaskHandle NITask
std::vector< std::unique_ptr< CircularStream > > ReadStreamPerChannel
std::vector< DigitalValueType > DigitalValues
auto GetBufferSizeInSamples() const noexcept
Bundles parameters to describe a NumericSampleStream's sampling properties.
Bundles parameters to describe a data stream's stream size.
DynExp's core class acts as the interface between the user interface and DynExp's internal data like ...
Definition: DynExpCore.h:127
Defines the base class for a hardware adapter object. Hardware adapters describe interfaces/connectio...
Configurator class for HardwareAdapterBase.
Parameter class for HardwareAdapterBase.
HardwareAdapterParamsBase(ItemIDType ID, const DynExpCore &Core)
Constructs the parameters for a HardwareAdapterBase instance.
const std::thread::id OwnerThreadID
Thread id of the thread which has constructed (and owns) this Object instance.
Definition: Object.h:2302
const ParamsBasePtrType Params
Pointer to the parameter class instance belonging to this Object instance.
Definition: Object.h:2303
Refer to ParamsBase::dispatch_tag.
Definition: Object.h:2018
Abstract base class for object parameter classes. Each class derived from class Object must be accomp...
Definition: Object.h:326
const ItemIDType ID
ID of the Object this parameter class instance belongs to.
Definition: Object.h:1779
const DynExpCore & Core
Reference to DynExp's core.
Definition: Object.h:1780
Tag for function dispatching mechanism within this class used when derived classes are not intended t...
Definition: Object.h:349
DynExp exceptions are derived from this class. It contains basic information about the cause of the e...
Definition: Exception.h:51
Exception(std::string Description, const ErrorType Type=ErrorType::Error, const int ErrorCode=-1, const std::source_location Location=std::source_location::current()) noexcept
Constructs an exception. Constructor is noexcept, although std::runtime_error() might throw std::bad_...
Definition: Exception.cpp:8
const ErrorType Type
DynExp error type from Util::ErrorType
Definition: Exception.h:105
const int ErrorCode
DynExp error code from DynExpErrorCodes::DynExpErrorCodes
Definition: Exception.h:106
Circular stream buffer to be used with the standard library's stream classes. Reading from or writing...
Definition: circularbuf.h:20
DynExp's hardware namespace contains the implementation of DynExp hardware adapters which extend DynE...
TriggerModeType
Type to determine the trigger mode. Not a strongly-typed enum to allow using the enumeration in a Dyn...
DynExp's instrument namespace contains the implementation of DynExp instruments which extend DynExp's...
Definition: Instrument.h:1254
std::unique_ptr< ParamsBase > ParamsBasePtrType
Alias for a pointer to the parameter system base class ParamsBase.
Definition: Object.h:1807
size_t ItemIDType
ID type of objects/items managed by DynExp.
ErrorType
DynExp's error types
Definition: Exception.h:15
std::vector< std::pair< TextType, ValueType > > TextValueListType
Type of a list containing key-value pairs where key is a text of type Util::TextType.
Definition: QtUtil.h:37
Accumulates include statements to provide a precompiled header.