Scope
DAQmxTask.cpp
1 #include "stdafx.h"
2 #include "DAQmxTask.h"
3 #include "helpers/ScopeException.h"
4 #include "ScopeDefines.h" // to get DAQMX_THROW_EXCEPTION
5 #include "helpers/Helpers.h"
6 
7 namespace DAQmx {
8 
9 bool CheckError(const int32& error) {
10  if ( DAQmxFailed(error) ) {
11  char perrorstring[512];
12  DAQmxGetErrorString(error, perrorstring, 512);
13  DAQmxGetExtendedErrorInfo(perrorstring, 512);
14  DBOUT(L"DAQmx exception: " << perrorstring)
15  if ( DAQMX_THROW_EXCEPTION )
16  throw scope::ScopeException(perrorstring);
17 
18  return true;
19  }
20  return false;
21 }
22 
23 double PredictSampleRate(const double& _desiredrate, const uint32_t& _nochannels, const double& _refclockrate, const int32& _mode) {
24  const double divider = std::floor(_refclockrate / (_desiredrate / static_cast<double>(_nochannels)));
25  const double rate1 = (_refclockrate / divider) * static_cast<double>(_nochannels);
26  const double rate2 = (_refclockrate / (divider+1)) * static_cast<double>(_nochannels);
27  switch (_mode) {
28  case DAQmx_Val_AI:
29  return rate1;
30  case DAQmx_Val_AO:
31  return (abs(rate1-_desiredrate) < abs(rate2-_desiredrate))?rate1:rate2;
32  default:
33  return -1;
34  }
35 }
36 
37 double CoerceSampleRates(const double& _desiredrate, const uint32_t& _nochannelsin, const uint32_t& _nochannelsout, const double& _refclockrate) {
38  double ratein = PredictSampleRate(_desiredrate, _nochannelsin, _refclockrate, DAQmx_Val_AI);
39  double rateout = PredictSampleRate(_desiredrate, _nochannelsout, _refclockrate, DAQmx_Val_AO);
40  if ( ratein == rateout )
41  return ratein;
42  else // rateout can only be == or < ratein (see PredictSampleRate)
43  return rateout;
44 }
45 
46 std::wstring ClockString(const scope::DaqTimingHelper::Mode& _timing, const std::wstring& _externalclocksource) {
47  switch ( _timing ) {
48  case scope::DaqTimingHelper::Mode::OnboardClock:
49  return L"OnboardClock";
50  break;
51  case scope::DaqTimingHelper::Mode::External:
52  return _externalclocksource;
53  break;
54  default:
55  return L""; // For reference clock leave blank!
56  }
57 }
58 
59 /*
60 static int32 GetTerminalNameWithDevPrefix(TaskHandle taskHandle, const char terminalName[], char triggerName[]) {
61  int32 error=0;
62  char device[256];
63  int32 productCategory;
64  uInt32 numDevices,i=1;
65 
66  CheckError(DAQmxGetTaskNumDevices(taskHandle,&numDevices));
67  while( i<=numDevices ) {
68  CheckError (DAQmxGetNthTaskDevice(taskHandle,i++,device,256));
69  CheckError (DAQmxGetDevProductCategory(device,&productCategory));
70  if( productCategory!=DAQmx_Val_CSeriesModule && productCategory!=DAQmx_Val_SCXIModule ) {
71  *triggerName++ = '/';
72  strcpy(triggerName,device); // TODO: Make these safe, use static arrays for char[] with fixed size
73  strcat(triggerName,"/");
74  strcat(triggerName,terminalName);
75  break;
76  }
77  }
78 
79  return error;
80 }*/
81 
82 CDAQmxTask::CDAQmxTask()
83  : task_handle(nullptr)
84 {}
85 
86 CDAQmxTask::~CDAQmxTask() {
87  if ( task_handle != nullptr ) // be super-safe here, destructor could be called by exception in constructor...
88  DAQmxClearTask(task_handle); // So, also do not call CheckError here, because we could get an exception, call the destructor by stack-unwinding, -> infinite loop
89 }
90 
91 void CDAQmxTask::CreateTask(const std::wstring& _name) {
92  CW2A char_name(_name.c_str());
93  CheckError(DAQmxCreateTask(char_name, &task_handle));
94 }
95 
96 bool CDAQmxTask::Created() const {
97  return task_handle != nullptr;
98 }
99 
100 void CDAQmxTask::ConfigureImplicitTiming(const int32& _mode, const int32& _samplesperchan) {
101  if ( (_mode != DAQmx_Val_FiniteSamps) && (_mode !=DAQmx_Val_ContSamps) )
102  throw std::exception("Timing mode invalid");
103 
104  CheckError(DAQmxCfgImplicitTiming(task_handle,
105  DAQmx_Val_ContSamps,
106  _samplesperchan));
107 }
108 
109 void CDAQmxTask::ConfigureSampleTiming(const std::wstring& _src, const float64& _rate, const int32& _samplesperchan, const int32& _samplingtype, const int32& _actedge) {
110  if ( (_samplingtype != DAQmx_Val_FiniteSamps) && (_samplingtype != DAQmx_Val_ContSamps) )
111  throw std::exception("Timing mode invalid");
112 
113  if ( (_actedge != DAQmx_Val_Rising) && (_actedge != DAQmx_Val_Falling) )
114  throw std::exception("Active edge invalid");
115 
116  CW2A char_src(_src.c_str());
117 
118  CheckError(DAQmxCfgSampClkTiming(task_handle,
119  char_src,
120  _rate,
121  _actedge,
122  _samplingtype,
123  _samplesperchan));
124 
125 
126 }
127 
128 void CDAQmxTask::ConfigureReferenceClock(const std::wstring& _src, const float64& _rate) {
129  CheckError(DAQmxSetTimingAttribute(task_handle, DAQmx_RefClk_Rate, _rate));
130  CW2A char_src(_src.c_str());
131  CheckError(DAQmxSetTimingAttribute(task_handle, DAQmx_RefClk_Src, char_src));
132 }
133 
134 void CDAQmxTask::ConfigureDigStartTrigger(const std::wstring& _src, const int32& _trigedge) {
135  if ( (_trigedge != DAQmx_Val_Rising) && (_trigedge != DAQmx_Val_Falling) )
136  throw std::exception("Trigger edge invalid");
137 
138  CW2A char_src(_src.c_str());
139 
140  CheckError(DAQmxCfgDigEdgeStartTrig(task_handle,
141  char_src,
142  _trigedge));
143 }
144 
145 void CDAQmxTask::Start(void) {
146  CheckError(DAQmxStartTask(task_handle));
147 }
148 
149 void CDAQmxTask::Stop(void) {
150  CheckError(DAQmxStopTask(task_handle));
151 }
152 
153 void CDAQmxTask::Clear(void) {
154  CheckError(DAQmxClearTask(task_handle));
155 }
156 
157 bool CDAQmxTask::IsTaskDone(void) {
158  bool32 isdone;
159  CheckError(DAQmxIsTaskDone(task_handle, &isdone));
160  return isdone!=0; // safe conversion from int to bool, simple casting causes compiler warning
161 }
162 
163 int32_t CDAQmxTask::WaitUntilDone(const float64& _waittime) {
164  return DAQmxWaitUntilTaskDone(task_handle, _waittime);
165 }
166 
167 void CDAQmxTask::ExportSignal(int32 _signal, const std::wstring& _terminal) {
168  CW2A char_terminal(_terminal.c_str());
169  DAQmxExportSignal(task_handle, _signal, char_terminal);
170 }
171 
172 void CDAQmxTask::SetWriteOffset(int32 posreltofirst) {
173  DAQmxSetWriteAttribute(task_handle, DAQmx_Write_RelativeTo, DAQmx_Val_FirstSample);
174  DAQmxSetWriteAttribute(task_handle, DAQmx_Write_Offset, posreltofirst);
175 }
176 
177 void CDAQmxTask::SetRegeneration(const bool& _regenerate) {
178  int32 mode = (_regenerate == true)?DAQmx_Val_AllowRegen:DAQmx_Val_DoNotAllowRegen;
179  DAQmxSetWriteAttribute(task_handle, DAQmx_Write_RegenMode, mode);
180 }
181 
182 void CDAQmxAnalogOutTask::CreateAOVoltageChannel(const std::wstring& devices,
183  const std::wstring& channelname,
184  float64 minval,
185  float64 maxval,
186  int32 units,
187  const std::wstring& customscalename) {
188  CW2A char_devices(devices.c_str());
189  CW2A char_channelname(channelname.c_str());
190  CW2A char_customscalename(customscalename.c_str());
191 
192  CheckError(DAQmxCreateAOVoltageChan(task_handle,
193  char_devices,
194  char_channelname,
195  minval,
196  maxval,
197  units,
198  char_customscalename));
199 }
200 
201 uInt32 CDAQmxAnalogOutTask::ConfigureBuffer(const uInt32& _sampsperchannel) {
202  DBOUT(L"CDAQmxAnalogOutTask::ConfigureBuffer requested samples per channel " << _sampsperchannel);
203  CheckError(DAQmxCfgOutputBuffer(task_handle, _sampsperchannel));
204  std::this_thread::sleep_for(std::chrono::milliseconds(100));
205  uInt32 value = 0;
206  CheckError(DAQmxGetBufferAttribute(task_handle, DAQmx_Buf_Output_BufSize, &value));
207  DBOUT(L"CDAQmxAnalogOutTask::ConfigureBuffer buffer size " << value);
208  return value;
209 }
210 
211 uInt32 CDAQmxAnalogOutTask::ConfigureOnboardBuffer(const uInt32& _sampsperchannel) {
212  CheckError(DAQmxSetBufOutputOnbrdBufSize(task_handle, _sampsperchannel));
213  uInt32 bufsize = 0;
214  CheckError(DAQmxGetBufOutputOnbrdBufSize(task_handle, &bufsize));
215  DBOUT(L"CDAQmxAnalogOutTask::ConfigureOnboardBuffer onboard buffer size " << bufsize);
216  return bufsize;
217 }
218 
219 void CDAQmxAnalogOutTask::UseOnlyOnboardMemory(const std::wstring& _channel) {
220  CW2A char_channel(_channel.c_str());
221  CheckError(DAQmxSetAOUseOnlyOnBrdMem(task_handle, char_channel, true));
222 }
223 
224 void CDAQmxAnalogOutTask::SetIdleOutputBehaviour(const std::wstring& _channel, const int32_t& _behav) {
225  CW2A char_channel(_channel.c_str());
226  CheckError(DAQmxSetAOIdleOutputBehavior(task_handle, char_channel, _behav));
227 }
228 
229 void CDAQmxAnalogOutTask::SetDataTransferCondition(const std::wstring& _channel, const int32_t& _cond) {
230  CW2A char_channel(_channel.c_str());
231  CheckError(DAQmxSetAODataXferReqCond(task_handle, char_channel, _cond));
232 }
233 
234 void CDAQmxAnalogOutTask::SetDataTransferMode(const std::wstring& _channel, const int32_t& _mode) {
235  CW2A char_channel(_channel.c_str());
236  CheckError(DAQmxSetAODataXferMech(task_handle, char_channel, _mode));
237 }
238 
239 int32 CDAQmxAnalogOutTask::WriteAnalog(const float64* _data, int32 _sampsperchan, bool _autostart, int32 _timeout, bool32 _layout) {
240  int32 writtensamples = -1;
241  CheckError(DAQmxWriteAnalogF64(task_handle
242  , _sampsperchan
243  , _autostart
244  , _timeout
245  , _layout
246  , _data
247  , &writtensamples, NULL));
248  return writtensamples;
249 }
250 
251 void CDAQmxAnalogOutTask::WriteAnalogScalar(const float64& _value, bool _autostart, int32 _timeout) {
252  CheckError(DAQmxWriteAnalogScalarF64(task_handle
253  , _autostart
254  , _timeout
255  , _value, NULL));
256 }
257 
258 int32 CDAQmxAnalogOutTask::WriteAnalogI16(const int16* _data, int32 _sampsperchan, bool _autostart, int32 _timeout, bool32 _layout) {
259  int32 writtensamples = -1;
260  CheckError(DAQmxWriteBinaryI16(task_handle
261  , _sampsperchan
262  , _autostart
263  , _timeout
264  , _layout
265  , _data
266  , &writtensamples, NULL));
267  return writtensamples;
268 }
269 
270 void CDAQmxDigitalOutTask::CreateDOChannel(const std::wstring& _devicelines, const std::wstring& _name) {
271  CW2A char_devicelines(_devicelines.c_str());
272  CW2A char_name(_name.c_str());
273 
274  CheckError(DAQmxCreateDOChan(task_handle
275  , char_devicelines
276  , char_name
277  , DAQmx_Val_ChanPerLine));
278  /*
279  DAQmx_Val_ChanPerLine 0 // One Channel For Each Line
280  DAQmx_Val_ChanForAllLines 1 // One Channel For All Lines
281  */
282 }
283 
284 int32 CDAQmxDigitalOutTask::WriteDigitalU8(const uInt8* _data, int32 _sampsperchan, bool _autostart, float64 _timeout, bool32 _layout) {
285  int32 writtensamples = -1;
286  CheckError(DAQmxWriteDigitalU8(task_handle
287  , _sampsperchan
288  , _autostart
289  , _timeout
290  , _layout
291  , _data
292  , &writtensamples, NULL));
293  return writtensamples;
294 }
295 
296 int32_t CDAQmxDigitalOutTask::WriteDigitalLines(const uInt8* _data, int32 _sampsperchan, bool _autostart, float64 _timeout, bool32 _layout) {
297  int32 writtensamples = -1;
298  CheckError(DAQmxWriteDigitalLines(task_handle
299  , _sampsperchan
300  , _autostart
301  , _timeout
302  , _layout
303  , _data
304  , &writtensamples, NULL));
305  return writtensamples;
306 }
307 
308 CDAQmxAnalogInTask::CDAQmxAnalogInTask() {
309  mtgen.seed((uint32_t)time(NULL));
310 }
311 
312 void CDAQmxAnalogInTask::CreateAIVoltageChannel(const std::wstring& _devicechannel,
313  const std::wstring& _name,
314  int32 _terminalconfig,
315  float64 _minval,
316  float64 _maxval,
317  int32 _units,
318  const std::wstring& _customscalename) {
319  CW2A char_devicechannel(_devicechannel.c_str());
320  CW2A char_name(_name.c_str());
321  CW2A char_customscalename(_customscalename.c_str());
322 
323  CheckError(DAQmxCreateAIVoltageChan(task_handle,
324  char_devicechannel,
325  char_name,
326  _terminalconfig,
327  _minval,
328  _maxval,
329  _units,
330  char_customscalename));
331 }
332 
333 void CDAQmxAnalogInTask::ConfigureBuffer(const uInt32& _sampsperchannel) {
334  CheckError(DAQmxCfgInputBuffer(task_handle, _sampsperchannel));
335  uInt32 value = 0;
336  CheckError(DAQmxGetBufferAttribute(task_handle, DAQmx_Buf_Input_BufSize, &value));
337  DBOUT(L"CDAQmxAnalogInTask::ConfigureBuffer buffer size " << value);
338 }
339 
340 int32 CDAQmxAnalogInTask::ReadU16(std::vector<uint16_t>& _data, const int32& _sampsperchan, const uint32_t& _channels, bool& _timedout, const float64& _timeout) {
341  int32 readsamples = 0;
342  int32_t status = DAQmxReadBinaryU16(task_handle
343  , _sampsperchan
344  , _timeout
345  , DAQmx_Val_GroupByChannel
346  , _data.data()
347  , static_cast<uInt32>(_data.size())
348  , &readsamples
349  , NULL);
350  _timedout = (status == DAQmxErrorOperationTimedOut);
351 
352  if ( _timedout ) // Avoid throwing an exception on time out
353  return -1;
354 
355  if ( CheckError(status) ) // CheckError could throw exception if DAQMX_THROW_EXCEPTION defined
356  return -1;
357  else
358  return readsamples;
359 }
360 
361 int32 CDAQmxAnalogInTask::ReadU16Dummy(std::vector<uint16_t>& _data, const int32& _sampsperchan, const uint32_t& _channels, bool& _timedout, const float64& _timeout) {
362  int32 readsamples = -1;
363  _timedout = false;
364  uint16_t d = 0;
365  std::generate(std::begin(_data), std::end(_data), [&d]() { return d++; } ); // or random: return static_cast<uint16_t>(mtgen());
366  return (_sampsperchan*_channels);
367 }
368 
369 int32 CDAQmxAnalogInTask::ReadI16(std::vector<int16_t>& _data, const int32& _sampsperchan, const uint32_t& _channels, bool& _timedout, const float64& _timeout) {
370  int32 readsamples = -1;
371  int32_t status = DAQmxReadBinaryI16(task_handle
372  , _sampsperchan
373  , _timeout
374  , DAQmx_Val_GroupByChannel
375  , _data.data()
376  , static_cast<uInt32>(_data.size())
377  , &readsamples
378  , NULL);
379 
380  _timedout = (status == DAQmxErrorOperationTimedOut);
381 
382  if ( _timedout ) // Avoid throwing an exception on time out
383  return -1;
384 
385  if ( CheckError(status) )
386  return -1;
387  else
388  return readsamples;
389  /*
390  DAQmx_Val_GroupByChannel 0 // Group by Channel
391  DAQmx_Val_GroupByScanNumber 1 // Group by Scan Number
392  */
393 }
394 
395 void CDAQmxCounterOutTask::CreateCOChannel(const std::wstring& _counter
396  , const std::wstring& _name
397  , int32 _idlestate
398  , float64 _initdelay
399  , float64 _lowtime
400  , float64 _hightime ) {
401  CW2A char_counter(_counter.c_str());
402  CW2A char_name(_name.c_str());
403 
404  CheckError(DAQmxCreateCOPulseChanTime(task_handle
405  , char_counter
406  , char_name
407  , DAQmx_Val_Seconds
408  , _idlestate
409  , _initdelay
410  , _lowtime
411  , _hightime));
412 }
413 
414 
415 }
Simple exception class for Scope.
Definition: ScopeException.h:9
std::wstring ClockString(const scope::DaqTimingHelper::Mode &_timing, const std::wstring &_externalclocksource)
Generates a string for the sample clock from daq_timing.
Definition: DAQmxTask.cpp:46
This is the include file for standard system include files, or project specific include files that ar...
bool CheckError(const int32 &error)
Checks return value of NI DAQmx function for error, prints out error information and throws an except...
Definition: DAQmxTask.cpp:9
#define DBOUT(s)
A debug output to the debug console.
Definition: helpers.h:153
This is basically a C++ wrapper around NI's DAQmx C library.
Definition: DAQmxTask.cpp:7
double PredictSampleRate(const double &_desiredrate, const uint32_t &_nochannels, const double &_refclockrate, const int32 &_mode)
Predicts the actual sampling rate.
Definition: DAQmxTask.cpp:23
Various helper functions and classes for Scope.
double CoerceSampleRates(const double &_desiredrate, const uint32_t &_nochannelsin, const uint32_t &_nochannelsout, const double &_refclockrate)
Calculates a sampling rate which both analog in and out can comply to.
Definition: DAQmxTask.cpp:37