11 {
"Buffered transfer", UseOnlyOnBrdMemType::UseMemoryBuffer },
12 {
"Direct transfer", UseOnlyOnBrdMemType::OnlyOnboardMemory }
26 {
"One task per channel", ChannelModeType::TaskPerChannel },
27 {
"Combine channels into one task", ChannelModeType::CombineChannels }
36 {
"Trigger disabled (start immediately)", TriggerModeType::Disabled },
37 {
"Trigger on rising edge", TriggerModeType::RisingEdge },
38 {
"Trigger on falling edge", TriggerModeType::FallingEdge }
47 auto RequiredSize = NIDAQSyms::DAQmxGetSysDevNames(
nullptr, 0);
49 throw NIDAQException(
"Error obtaining buffer size for enumerating NIDAQmx devices.", RequiredSize);
51 std::string DeviceList;
52 DeviceList.resize(RequiredSize);
55 auto Result = NIDAQSyms::DAQmxGetSysDevNames(DeviceList.data(), Util::NumToT<NIDAQSyms::uInt32>(DeviceList.size()));
57 throw NIDAQException(
"Error enumerating NIDAQmx devices.", Result);
61 std::replace(DeviceList.begin(), DeviceList.end(),
',',
' ');
62 std::istringstream ss(DeviceList);
63 std::vector<std::string> DeviceDescriptors{ std::istream_iterator<std::string>(ss), std::istream_iterator<std::string>() };
65 return DeviceDescriptors;
69 : Buffer(BufferSize), Stream(&Buffer)
71 Stream.exceptions(std::iostream::failbit | std::iostream::badbit);
92 NIDAQSyms::DAQmxStopTask(
NITask);
93 NIDAQSyms::DAQmxClearTask(
NITask);
119 : HardwareAdapterBase(OwnerThreadID, std::move(Params))
137 auto Result = NIDAQSyms::DAQmxCreateDIChan(Task->NITask, ChannelName.data(),
"", DAQmx_Val_ChanPerLine);
141 auto DerivedParams = dynamic_Params_cast<NIDAQHardwareAdapter>(
GetParams());
142 Task->AddChannel(Handle, DerivedParams->StreamSizeParams.StreamSize);
144 Result = NIDAQSyms::DAQmxSetReadReadAllAvailSamp(Task->NITask,
true);
146 Result = NIDAQSyms::DAQmxSetReadOverWrite(Task->NITask, DAQmx_Val_OverwriteUnreadSamps);
150 DerivedParams->NumericSampleStreamParams.SamplingMode);
167 auto Result = NIDAQSyms::DAQmxCreateDOChan(Task->NITask, ChannelName.data(),
"", DAQmx_Val_ChanPerLine);
170 auto DerivedParams = dynamic_Params_cast<NIDAQHardwareAdapter>(
GetParams());
171 Task->AddChannel(Handle, DerivedParams->StreamSizeParams.StreamSize);
173 if (DerivedParams->StreamSizeParams.StreamSize > 1)
175 Result = NIDAQSyms::DAQmxSetBufOutputBufSize(Task->NITask, DerivedParams->StreamSizeParams.StreamSize);
177 Result = NIDAQSyms::DAQmxSetWriteRelativeTo(Task->NITask, DAQmx_Val_FirstSample);
181 Result = NIDAQSyms::DAQmxSetDOUseOnlyOnBrdMem(Task->NITask, ChannelName.data(),
182 UseOnlyOnBrdMem == NIDAQOutputPortParamsExtension::UseOnlyOnBrdMemType::OnlyOnboardMemory);
186 DerivedParams->NumericSampleStreamParams.SamplingMode);
189 if (DerivedParams->StreamSizeParams.StreamSize <= 1)
196 double MinValue,
double MaxValue,
double Timeout, int32_t TerminalConfig)
const
203 auto Result = NIDAQSyms::DAQmxCreateAIVoltageChan(Task->NITask, ChannelName.data(),
"", TerminalConfig,
204 MinValue, MaxValue, DAQmx_Val_Volts,
nullptr);
208 auto DerivedParams = dynamic_Params_cast<NIDAQHardwareAdapter>(
GetParams());
209 Task->AddChannel(Handle, DerivedParams->StreamSizeParams.StreamSize);
211 Result = NIDAQSyms::DAQmxSetReadReadAllAvailSamp(Task->NITask,
true);
213 Result = NIDAQSyms::DAQmxSetReadOverWrite(Task->NITask, DAQmx_Val_OverwriteUnreadSamps);
217 DerivedParams->NumericSampleStreamParams.SamplingMode);
234 auto Result = NIDAQSyms::DAQmxCreateAOVoltageChan(Task->NITask, ChannelName.data(),
"",
235 MinValue, MaxValue, DAQmx_Val_Volts,
nullptr);
238 auto DerivedParams = dynamic_Params_cast<NIDAQHardwareAdapter>(
GetParams());
239 Task->AddChannel(Handle, DerivedParams->StreamSizeParams.StreamSize);
241 if (DerivedParams->StreamSizeParams.StreamSize > 1)
243 Result = NIDAQSyms::DAQmxSetBufOutputBufSize(Task->NITask, DerivedParams->StreamSizeParams.StreamSize);
245 Result = NIDAQSyms::DAQmxSetWriteRelativeTo(Task->NITask, DAQmx_Val_FirstSample);
249 Result = NIDAQSyms::DAQmxSetAOUseOnlyOnBrdMem(Task->NITask, ChannelName.data(),
250 UseOnlyOnBrdMem == NIDAQOutputPortParamsExtension::UseOnlyOnBrdMemType::OnlyOnboardMemory);
254 DerivedParams->NumericSampleStreamParams.SamplingMode);
257 if (DerivedParams->StreamSizeParams.StreamSize <= 1)
275 if (Task->GetNumSamples() > std::numeric_limits<NIDAQSyms::int32>::max())
277 "Number of samples to read must not exceed " + std::to_string(std::numeric_limits<NIDAQSyms::int32>::max()) +
".")));
280 NIDAQSyms::uInt64 Position{};
281 auto Result = NIDAQSyms::DAQmxGetReadCurrReadPos(Task->NITask, &Position);
286 std::vector<NIDAQTask::DigitalValueType> Data;
287 Data.resize(Task->GetBufferSizeInSamples(), 0);
289 NIDAQSyms::int32 NumSamplesRead = 0;
290 NIDAQSyms::int32 BytesPerSample = 0;
291 Result = NIDAQSyms::DAQmxReadDigitalLines(Task->NITask, DAQmx_Val_Auto, Task->GetTimeout(), DAQmx_Val_GroupByChannel,
292 Data.data(), Util::NumToT<NIDAQSyms::uInt32>(Task->GetBufferSizeInSamples() * Task->GetSampleSizeInBytes()),
293 &NumSamplesRead, &BytesPerSample,
nullptr);
294 if (Result != DAQmxErrorOperationTimedOut)
298 if (BytesPerSample != Task->GetSampleSizeInBytes())
300 "Received data from DAQmxReadDigitalLines() which does not correspond to the expected memory layout.")));
302 if (!Task->IsCombined())
303 Data.resize(NumSamplesRead);
306 for (decltype(Task->NumChannels) i = 0; i < Task->NumChannels; ++i)
307 Task->ReadStreamPerChannel[i]->Stream.write(
reinterpret_cast<const char*
>(Data.data() + i * NumSamplesRead), NumSamplesRead * Task->GetSampleSizeInBytes());
309 const auto NumBytesToRead = Task->ReadStreamPerChannel[Task->GetChannelIndex(ChannelHandle)]->Buffer.gsize();
311 Data.resize(NumBytesToRead / Task->GetSampleSizeInBytes());
312 Task->ReadStreamPerChannel[Task->GetChannelIndex(ChannelHandle)]->Stream.read(
reinterpret_cast<char*
>(Data.data()), NumBytesToRead);
313 Task->ReadStreamPerChannel[Task->GetChannelIndex(ChannelHandle)]->Clear();
328 if (Values.size() > std::numeric_limits<NIDAQSyms::int32>::max())
330 "Number of samples to write must not exceed " + std::to_string(std::numeric_limits<NIDAQSyms::int32>::max()) +
".")));
332 NIDAQSyms::int32 NumSamplesWritten = 0;
333 if (!Task->IsCombined())
335 auto Result = NIDAQSyms::DAQmxWriteDigitalLines(Task->NITask, Util::NumToT<NIDAQSyms::int32>(Values.size()),
false, Task->GetTimeout(),
336 DAQmx_Val_GroupByChannel, Values.data(), &NumSamplesWritten,
nullptr);
341 auto Destiny = Task->DigitalValues | std::views::drop(Task->GetChannelIndex(ChannelHandle) * Task->GetNumSamples());
342 auto NewValues = Values | std::views::take(Task->GetNumSamples());
343 std::ranges::copy(NewValues, Destiny.begin());
344 if (NewValues.size() < Task->GetNumSamples())
345 std::ranges::fill_n(Destiny.begin() + NewValues.size(), Task->GetNumSamples() - NewValues.size(), 0);
347 auto Result = NIDAQSyms::DAQmxWriteDigitalLines(Task->NITask, Util::NumToT<NIDAQSyms::int32>(Task->GetNumSamples()),
false, Task->GetTimeout(),
348 DAQmx_Val_GroupByChannel, Task->DigitalValues.data(), &NumSamplesWritten,
nullptr);
352 return NumSamplesWritten;
360 if (Task->GetNumSamples() > std::numeric_limits<NIDAQSyms::int32>::max())
362 "Number of samples to read must not exceed " + std::to_string(std::numeric_limits<NIDAQSyms::int32>::max()) +
".")));
365 NIDAQSyms::uInt64 Position{};
366 auto Result = NIDAQSyms::DAQmxGetReadCurrReadPos(Task->NITask, &Position);
371 std::vector<NIDAQTask::AnalogValueType> Data;
372 Data.resize(Task->GetBufferSizeInSamples(), .0);
374 NIDAQSyms::int32 NumSamplesRead = 0;
375 Result = NIDAQSyms::DAQmxReadAnalogF64(Task->NITask, DAQmx_Val_Auto, Task->GetTimeout(), DAQmx_Val_GroupByChannel,
376 Data.data(), Util::NumToT<NIDAQSyms::uInt32>(Task->GetNumSamples()), &NumSamplesRead,
nullptr);
377 if (Result != DAQmxErrorOperationTimedOut)
380 if (!Task->IsCombined())
381 Data.resize(NumSamplesRead);
384 for (decltype(Task->NumChannels) i = 0; i < Task->NumChannels; ++i)
385 Task->ReadStreamPerChannel[i]->Stream.write(
reinterpret_cast<const char*
>(Data.data() + i * NumSamplesRead), NumSamplesRead * Task->GetSampleSizeInBytes());
387 const auto NumBytesToRead = Task->ReadStreamPerChannel[Task->GetChannelIndex(ChannelHandle)]->Buffer.gsize();
389 Data.resize(NumBytesToRead / Task->GetSampleSizeInBytes());
390 Task->ReadStreamPerChannel[Task->GetChannelIndex(ChannelHandle)]->Stream.read(
reinterpret_cast<char*
>(Data.data()), NumBytesToRead);
391 Task->ReadStreamPerChannel[Task->GetChannelIndex(ChannelHandle)]->Clear();
406 if (Values.size() > std::numeric_limits<NIDAQSyms::int32>::max())
408 "Number of samples to write must not exceed " + std::to_string(std::numeric_limits<NIDAQSyms::int32>::max()) +
".")));
410 NIDAQSyms::int32 NumSamplesWritten = 0;
411 if (!Task->IsCombined())
413 auto Result = NIDAQSyms::DAQmxWriteAnalogF64(Task->NITask, Util::NumToT<NIDAQSyms::int32>(Values.size()),
false, Task->GetTimeout(),
414 DAQmx_Val_GroupByChannel, Values.data(), &NumSamplesWritten,
nullptr);
419 auto Destiny = Task->AnalogValues | std::views::drop(Task->GetChannelIndex(ChannelHandle) * Task->GetNumSamples());
420 auto NewValues = Values | std::views::take(Task->GetNumSamples());
421 std::ranges::copy(NewValues, Destiny.begin());
422 if (NewValues.size() < Task->GetNumSamples())
423 std::ranges::fill_n(Destiny.begin() + NewValues.size(), Task->GetNumSamples() - NewValues.size(), .0);
425 auto Result = NIDAQSyms::DAQmxWriteAnalogF64(Task->NITask, Util::NumToT<NIDAQSyms::int32>(Task->GetNumSamples()),
false, Task->GetTimeout(),
426 DAQmx_Val_GroupByChannel, Task->AnalogValues.data(), &NumSamplesWritten,
nullptr);
430 return NumSamplesWritten;
507 uint32_t RequiredSize = NIDAQSyms::DAQmxGetExtendedErrorInfo(
nullptr, 0);
508 std::string ErrorString(RequiredSize,
'\0');
509 NIDAQSyms::DAQmxGetExtendedErrorInfo(ErrorString.data(), RequiredSize);
526 if (Result == DAQmxErrorSamplesNoLongerAvailable)
531 if (Result != DAQmxErrorSamplesNotYetAvailable)
538 NIDAQSyms::TaskHandle Handle =
nullptr;
540 auto Result = NIDAQSyms::DAQmxCreateTask(
"", &Handle);
544 "Unexpected error creating an NIDAQ task: nullptr received from DAQmxCreateTask().")));
554 auto NIDAQSamplingMode = SamplingMode == DynExpInstr::NumericSampleStreamParamsExtension::SamplingModeType::Single ?
557 auto Result = NIDAQSyms::DAQmxCfgSampClkTiming(Task->
NITask,
"", SamplingRate, DAQmx_Val_Rising,
569 if (TriggerMode != NIDAQHardwareAdapterParams::TriggerModeType::RisingEdge && TriggerMode != NIDAQHardwareAdapterParams::TriggerModeType::FallingEdge)
572 auto Result = NIDAQSyms::DAQmxCfgDigEdgeStartTrig(Task->
NITask, TriggerChannelName.data(),
573 TriggerMode == NIDAQHardwareAdapterParams::TriggerModeType::RisingEdge ? DAQmx_Val_Rising : DAQmx_Val_Falling);
581 auto Result = NIDAQSyms::DAQmxStartTask(Task->
NITask);
587 auto Result = NIDAQSyms::DAQmxStopTask(Task->
NITask);
599 NIDAQSyms::bool32 IsDone{};
600 auto Result = NIDAQSyms::DAQmxIsTaskDone(Task->
NITask, &IsDone);
608 return std::hash<std::string_view>()(ChannelName);
613 return Tasks.find(ChannelHandle) !=
Tasks.cend();
623 auto Task =
Tasks.find(ChannelHandle);
624 if (Task ==
Tasks.cend())
626 "The specified ChannelHandle does not exist.")));
628 return Task->second.get();
642 "There is already a task for channel " + std::string(ChannelName) +
".")));
646 Tasks.emplace(std::make_pair(Handle, std::move(TaskHandle)));
658 auto DerivedParams = dynamic_Params_cast<NIDAQHardwareAdapter>(
GetParams());
660 if (DerivedParams->ChannelMode == NIDAQHardwareAdapterParams::ChannelModeType::CombineChannels && DerivedParams->StreamSizeParams.StreamSize > 1)
667 auto Task =
Tasks.begin()->second;
668 if (Task->GetType() != Type)
670 "Only channels of the same type can be combined into one task.")));
689 return Tasks.erase(ChannelHandle) > 0;
Implementation of a hardware adapter to control National Instruments NIDAQmx hardware.
static Util::TextValueListType< ChannelModeType > ChannelModeTypeStrList()
static Util::TextValueListType< TriggerModeType > TriggerModeTypeStrList()
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
virtual ~NIDAQHardwareAdapter()
ChannelHandleType CreateTaskIfNotExistsUnsafe(std::string_view ChannelName, NIDAQTask::ChannelType Type) const
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
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
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
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?
void DisableUserEditable()
static Util::TextValueListType< UseOnlyOnBrdMemType > UseOnlyOnBrdMemTypeStrList()
bool IsCombined() const noexcept
std::vector< AnalogValueType > AnalogValues
NIDAQTask(ChannelType Type, NIDAQSyms::TaskHandle NITask, NIDAQHardwareAdapterParams::ChannelModeType ChannelMode=NIDAQHardwareAdapterParams::ChannelModeType::TaskPerChannel)
std::map< ChannelHandleType, uint32_t > ChannelIndexMap
const NIDAQHardwareAdapterParams::ChannelModeType ChannelMode
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
SamplingModeType SamplingMode
auto GetBufferSizeInSamples() const noexcept
SamplingModeType
Type to determine how to record/play back to/from the stream.
static constexpr auto HardwareOperationTimeout
Default timeout used to lock the mutex provided by the base class Util::ILockable to synchronize acce...
void ThrowExceptionUnsafe(std::exception_ptr Exception) const
Stores Exception in LastException, wraps it in a Util::ForwardedException and throws the wrapped exce...
auto GetExceptionUnsafe() const
Getter for LastException.
ParamsConstTypeSyncPtrType GetParams(const std::chrono::milliseconds Timeout=GetParamsTimeoutDefault) const
Locks the mutex of the parameter class instance Params assigned to this Object instance and returns a...
Refer to ParamsBase::dispatch_tag.
static void DisableUserEditable(ParamBase &Param) noexcept
Sets the UserEditable property of the parameter Param to false. Refer to ParamBase::UserEditable.
Logs events like errors and writes them immediately to a HTML file in a human-readable format....
void Log(const std::string &Message, const ErrorType Type=ErrorType::Info, const size_t Line=0, const std::string &Function="", const std::string &File="", const int ErrorCode=0, const std::stacktrace &Trace={}) noexcept
Logs an event from information specified manually.
LockType AcquireLock(const std::chrono::milliseconds Timeout=DefaultTimeout) const
Locks the internal mutex. Blocks until the mutex is locked or until the timeout duration is exceeded.
An invalid argument like a null pointer has been passed to a function.
Data to operate on is invalid for a specific purpose. This indicates a corrupted data structure or fu...
Thrown when some operation or feature is temporarily or permanently not available.
Thrown when a requested ressource does not exist.
Thrown when a numeric operation would result in an overflow (e.g. due to incompatible data types)
DynExp's hardware namespace contains the implementation of DynExp hardware adapters which extend DynE...
std::unique_ptr< ParamsBase > ParamsBasePtrType
Alias for a pointer to the parameter system base class ParamsBase.
void ForwardException(std::exception_ptr e)
Wraps the exception passed to the function in a ForwardedException and throws the ForwardedException....
std::string TrimTrailingZeros(const std::string &Str)
Removes trailing zeros ('\0') from a string.
std::vector< std::pair< TextType, ValueType > > TextValueListType
Type of a list containing key-value pairs where key is a text of type Util::TextType.
Accumulates include statements to provide a precompiled header.
CircularStream(size_t BufferSize)