RTXI  3.0.0
The Real-Time eXperiment Interface Reference Manual
nidaq_driver.cpp
Go to the documentation of this file.
1 
2 extern "C"
3 {
4 #include <NIDAQmx.h>
5 }
6 
7 #include <tuple>
8 #include <utility>
9 
10 #include <fmt/core.h>
11 
12 #include "daq.hpp"
13 
14 const std::string_view DEFAULT_DRIVER_NAME = "National Instruments";
15 
16 std::vector<std::string> split_string(const std::string& buffer,
17  const std::string& delim)
18 {
19  if (buffer.empty()) {
20  return {};
21  }
22  size_t pos_start = 0;
23  size_t pos_end = buffer.find(delim, pos_start);
24  if (pos_end == std::string::npos) {
25  return {buffer};
26  }
27  std::string token;
28  std::vector<std::string> split_tokens;
29  const size_t delim_len = delim.size();
30  while (true) {
31  token = buffer.substr(pos_start, pos_end - pos_start);
32  pos_start = pos_end + delim_len;
33  split_tokens.push_back(token);
34  pos_end = buffer.find(delim, pos_start);
35  if (pos_end == std::string::npos) {
36  break;
37  }
38  }
39  token = buffer.substr(pos_start, buffer.size() - pos_start);
40  split_tokens.push_back(token);
41  return split_tokens;
42 }
43 
44 std::vector<std::string> physical_channel_names(
45  const std::string& device, DAQ::ChannelType::type_t chan_type)
46 {
47  int32_t (*func)(const char*, char*, uint32_t) = nullptr;
48  switch (chan_type) {
50  func = &DAQmxGetDevAOPhysicalChans;
51  break;
53  func = &DAQmxGetDevAIPhysicalChans;
54  break;
56  func = &DAQmxGetDevDOLines;
57  break;
59  func = &DAQmxGetDevDILines;
60  break;
61  default:
62  return {};
63  }
64  std::string channel_names;
65  const int32_t buff_size = func(device.c_str(), nullptr, 0);
66  if (buff_size <= 0) {
67  return {};
68  }
69  channel_names.resize(static_cast<size_t>(buff_size));
70  func(device.c_str(), channel_names.data(), static_cast<uint32_t>(buff_size));
71  return split_string(channel_names, ", ");
72 }
73 
74 void printError(int32_t status)
75 {
76  if (status == 0) {
77  return;
78  }
79  ERROR_MSG("NIDAQ ERROR : code {}", status);
80  const int32_t error_size = DAQmxGetErrorString(status, nullptr, 0);
81  if (error_size < 0) {
82  ERROR_MSG("Unable to get code message");
83  return;
84  }
85  std::string err_str(static_cast<size_t>(error_size), '\0');
86  const int32_t errcode = DAQmxGetErrorString(
87  status, err_str.data(), static_cast<uint32_t>(error_size));
88  if (errcode < 0) {
89  ERROR_MSG("Unable to parse message");
90  return;
91  }
92  ERROR_MSG("Message : {}", err_str);
93 }
94 
95 std::string physical_card_name(const std::string& device_name)
96 {
97  const int32_t err = DAQmxGetDevProductType(device_name.c_str(), nullptr, 0);
98  std::string result(static_cast<size_t>(err), '\0');
99  DAQmxGetDevProductType(
100  device_name.c_str(), result.data(), static_cast<uint32_t>(result.size()));
101  return result;
102 }
103 
105 {
106  explicit physical_channel_t(std::string chan_name,
107  DAQ::ChannelType::type_t chan_type,
108  size_t chan_id)
109  : name(std::move(chan_name))
110  , type(chan_type)
111  , id(chan_id)
112  {
113  }
114  int32_t addToTask(TaskHandle task_handle) const;
115  std::string name;
117  size_t id;
118  int32_t reference = DAQmx_Val_RSE;
119  double offset = 0.0;
120  double gain = 1.0;
121  size_t range_index = 0;
122  size_t units_index = 0;
123  bool active = false;
124 };
125 
126 int32_t physical_channel_t::addToTask(TaskHandle task_handle) const
127 {
128  int32_t err = 0;
129  const std::string units = DAQ::get_default_units().at(units_index);
130  auto [min, max] = DAQ::get_default_ranges().at(range_index);
131  // DAQmxStopTask(task_handle);
132  switch (type) {
134  if (units == "volts") {
135  err = DAQmxCreateAIVoltageChan(task_handle,
136  name.c_str(),
137  nullptr,
138  reference,
139  min,
140  max,
141  DAQmx_Val_Volts,
142  nullptr);
143  } else if (units == "amps") {
144  err = DAQmxCreateAICurrentChan(task_handle,
145  name.c_str(),
146  nullptr,
147  reference,
148  min,
149  max,
150  DAQmx_Val_Amps,
151  DAQmx_Val_Default,
152  0,
153  nullptr);
154  } else {
155  ERROR_MSG("NIDAQ : Virtual Channel Creation : Unknown units {}", units);
156  }
157  printError(err);
158  break;
160  if (units == "volts") {
161  err = DAQmxCreateAOVoltageChan(task_handle,
162  name.c_str(),
163  nullptr,
164  min,
165  max,
166  DAQmx_Val_Volts,
167  nullptr);
168  } else if (units == "amps") {
169  err = DAQmxCreateAOCurrentChan(task_handle,
170  name.c_str(),
171  nullptr,
172  min,
173  max,
174  DAQmx_Val_Amps,
175  nullptr);
176  } else {
177  ERROR_MSG("NIDAQ : Virtual Channel Creation : Unknown units {}", units);
178  }
179  printError(err);
180  break;
182  err = DAQmxCreateDIChan(
183  task_handle, name.c_str(), nullptr, DAQmx_Val_ChanPerLine);
184  printError(err);
185  break;
187  err = DAQmxCreateDOChan(
188  task_handle, name.c_str(), nullptr, DAQmx_Val_ChanPerLine);
189  printError(err);
190  break;
191  default:
192  ERROR_MSG("NIDAQ_DRIVER : Channel Type Unknown");
193  break;
194  }
195  // DAQmxTaskControl(task_handle, DAQmx_Val_Task_Commit);
196  // DAQmxStartTask(task_handle);
197  return err;
198 }
199 
200 class Device final : public DAQ::Device
201 {
202 public:
203  Device(const Device&) = default;
204  Device(Device&&) = delete;
205  Device& operator=(const Device&) = delete;
206  Device& operator=(Device&&) = delete;
207  Device(const std::string& dev_name,
208  const std::vector<IO::channel_t>& channels,
209  std::string internal_name);
210  ~Device() final;
211 
212  size_t getChannelCount(DAQ::ChannelType::type_t type) const final;
213  bool getChannelActive(DAQ::ChannelType::type_t type,
214  DAQ::index_t index) const final;
215  int setChannelActive(DAQ::ChannelType::type_t type,
216  DAQ::index_t index,
217  bool state) final;
218  size_t getAnalogRangeCount(DAQ::index_t index) const final;
219  size_t getAnalogReferenceCount(DAQ::index_t index) const final;
220  size_t getAnalogUnitsCount(DAQ::index_t index) const final;
221  size_t getAnalogDownsample(DAQ::ChannelType::type_t type,
222  DAQ::index_t index) const final;
223  std::string getAnalogRangeString(DAQ::ChannelType::type_t type,
224  DAQ::index_t index,
225  DAQ::index_t range) const final;
226  std::string getAnalogReferenceString(DAQ::ChannelType::type_t type,
227  DAQ::index_t index,
228  DAQ::index_t reference) const final;
229  std::string getAnalogUnitsString(DAQ::ChannelType::type_t type,
230  DAQ::index_t index,
231  DAQ::index_t units) const final;
232  double getAnalogGain(DAQ::ChannelType::type_t type,
233  DAQ::index_t index) const final;
234  double getAnalogZeroOffset(DAQ::ChannelType::type_t type,
235  DAQ::index_t index) const final;
236  DAQ::index_t getAnalogRange(DAQ::ChannelType::type_t type,
237  DAQ::index_t index) const final;
238  DAQ::index_t getAnalogReference(DAQ::ChannelType::type_t type,
239  DAQ::index_t index) const final;
240  DAQ::index_t getAnalogUnits(DAQ::ChannelType::type_t type,
241  DAQ::index_t index) const final;
242  DAQ::index_t getAnalogOffsetUnits(DAQ::ChannelType::type_t type,
243  DAQ::index_t index) const final;
244  int setAnalogGain(DAQ::ChannelType::type_t type,
245  DAQ::index_t index,
246  double gain) final;
247  int setAnalogRange(DAQ::ChannelType::type_t type,
248  DAQ::index_t index,
249  DAQ::index_t range) final;
250  int setAnalogZeroOffset(DAQ::ChannelType::type_t type,
251  DAQ::index_t index,
252  double offset) final;
253  int setAnalogReference(DAQ::ChannelType::type_t type,
254  DAQ::index_t index,
255  DAQ::index_t reference) final;
256  int setAnalogUnits(DAQ::ChannelType::type_t type,
257  DAQ::index_t index,
258  DAQ::index_t units) final;
259  int setAnalogOffsetUnits(DAQ::ChannelType::type_t type,
260  DAQ::index_t index,
261  DAQ::index_t units) final;
262  int setAnalogDownsample(DAQ::ChannelType::type_t type,
263  DAQ::index_t index,
264  size_t downsample) final;
265  int setAnalogCounter(DAQ::ChannelType::type_t type, DAQ::index_t index) final;
266  int setAnalogCalibrationValue(DAQ::ChannelType::type_t type,
267  DAQ::index_t index,
268  double value) final;
269  double getAnalogCalibrationValue(DAQ::ChannelType::type_t type,
270  DAQ::index_t index) const final;
271  int setAnalogCalibrationActive(DAQ::ChannelType::type_t type,
272  DAQ::index_t index,
273  bool state) final;
274  bool getAnalogCalibrationActive(DAQ::ChannelType::type_t type,
275  DAQ::index_t index) const final;
276  bool getAnalogCalibrationState(DAQ::ChannelType::type_t type,
277  DAQ::index_t index) const final;
278  int setDigitalDirection(DAQ::index_t index, DAQ::direction_t direction) final;
279 
280  void read() final;
281  void write() final;
282 
283 private:
284  std::array<TaskHandle, DAQ::ChannelType::UNKNOWN> task_list {};
285  std::array<std::vector<physical_channel_t*>, DAQ::ChannelType::UNKNOWN>
286  active_channels;
287  std::string internal_dev_name;
288  std::array<std::vector<physical_channel_t>, 4> physical_channels_registry;
289  std::array<DAQ::analog_range_t, 7> default_ranges = DAQ::get_default_ranges();
290  std::array<std::string, 2> default_units = DAQ::get_default_units();
291  std::tuple<std::vector<double>,
292  std::vector<double>,
293  std::vector<uint8_t>,
294  std::vector<uint8_t>>
295  buffer_arrays;
296 };
297 
298 class Driver : public DAQ::Driver
299 {
300 public:
301  static DAQ::Driver* getInstance();
302  void loadDevices() final;
303  void unloadDevices() final;
304  std::vector<DAQ::Device*> getDevices() final;
305 
306 private:
307  Driver();
308  std::vector<Device> nidaq_devices;
309 };
310 
311 Device::Device(const std::string& dev_name,
312  const std::vector<IO::channel_t>& channels,
313  std::string internal_name)
314  : DAQ::Device(dev_name, channels)
315  , internal_dev_name(std::move(internal_name))
316 {
317  size_t inputs_count = 0;
318  size_t outputs_count = 0;
319  std::string task_name;
320  for (size_t type = 0; type < DAQ::ChannelType::UNKNOWN; type++) {
321  task_name = DAQ::ChannelType::type2string(
322  static_cast<DAQ::ChannelType::type_t>(type));
323  if (DAQmxCreateTask(task_name.c_str(), &task_list.at(type)) < 0) {
324  ERROR_MSG("NIDAQ::Device : Unable to create {} task for device {}",
325  task_name,
326  dev_name);
327  } else {
328  switch (type) {
331  DAQmxCfgInputBuffer(task_list.at(type), 1);
332  DAQmxSetReadOverWrite(task_list.at(type),
333  DAQmx_Val_OverwriteUnreadSamps);
334  break;
337  DAQmxCfgOutputBuffer(task_list.at(type), 1);
338  break;
339  default:
340  break;
341  }
342  }
343  }
344  std::vector<std::string> chan_names;
345  for (size_t type = 0; type < physical_channels_registry.size(); type++) {
346  chan_names = physical_channel_names(
347  internal_dev_name, static_cast<DAQ::ChannelType::type_t>(type));
348  for (const auto& chan_name : chan_names) {
349  physical_channels_registry.at(type).emplace_back(
350  chan_name,
351  static_cast<DAQ::ChannelType::type_t>(type),
352  type % 2 == 0 ? inputs_count : outputs_count);
353  type % 2 == 0 ? inputs_count++ : outputs_count++;
354  }
355  }
356  std::get<DAQ::ChannelType::AI>(buffer_arrays)
358  std::get<DAQ::ChannelType::AO>(buffer_arrays)
360  std::get<DAQ::ChannelType::DI>(buffer_arrays)
362  std::get<DAQ::ChannelType::DO>(buffer_arrays)
364 
365  for (auto& task : task_list) {
366  DAQmxSetSampTimingType(task, DAQmx_Val_OnDemand);
367  DAQmxTaskControl(task, DAQmx_Val_Task_Commit);
368  DAQmxStartTask(task);
369  }
370  this->setActive(/*act=*/true);
371 }
372 
374 {
375  for (const auto& task : task_list) {
376  DAQmxStopTask(task);
377  DAQmxClearTask(task);
378  }
379 }
380 
382 {
383  return physical_channels_registry.at(type).size();
384 }
385 
387  DAQ::index_t index) const
388 {
389  return physical_channels_registry.at(type).at(index).active;
390 }
391 
393  DAQ::index_t index,
394  bool state)
395 {
396  // Unfortunately National Instruments DAQ cards under nidaqmx library best
397  // work under tasks, which are more efficient at reading when started before
398  // any action. The bad thing is that NI does not make it easy to just remove a
399  // channel. Adding is easy, but removing means clearing the task, then
400  // starting the task again, and adding all of the other channels.
401  int32_t err = 0;
402  if (physical_channels_registry.at(type).at(index).active == state) {
403  return err;
404  }
405  DAQmxStopTask(task_list.at(type));
406  if (state) {
407  err = physical_channels_registry.at(type).at(index).addToTask(
408  task_list.at(type));
409  physical_channels_registry.at(type).at(index).active = err == 0;
410  } else {
411  physical_channels_registry.at(type).at(index).active = false;
412  DAQmxClearTask(task_list.at(type));
413  err = DAQmxCreateTask(DAQ::ChannelType::type2string(type).c_str(),
414  &task_list.at(type));
415  switch (type) {
418  DAQmxCfgInputBuffer(task_list.at(type), 1);
419  DAQmxSetReadOverWrite(task_list.at(type),
420  DAQmx_Val_OverwriteUnreadSamps);
421  break;
424  DAQmxCfgOutputBuffer(task_list.at(type), 1);
425  break;
426  default:
427  break;
428  }
429  if (err != 0) {
430  for (auto& channel : physical_channels_registry.at(type)) {
431  channel.active = false;
432  }
433  active_channels.at(type).clear();
434  return err;
435  }
436  for (const auto& channel : physical_channels_registry.at(type)) {
437  if (!channel.active) {
438  continue;
439  }
440  err = channel.addToTask(task_list.at(type));
441  if (err != 0) {
442  break;
443  }
444  }
445  }
446  // active channels is an optimization for faster reading/writing in realtime.
447  active_channels.at(type).clear();
448  for (auto& channel : physical_channels_registry.at(type)) {
449  if (channel.active) {
450  active_channels.at(type).push_back(&channel);
451  }
452  }
453  printError(DAQmxSetSampTimingType(task_list.at(type), DAQmx_Val_OnDemand));
454  printError(DAQmxTaskControl(task_list.at(type), DAQmx_Val_Task_Commit));
455  printError(DAQmxStartTask(task_list.at(type)));
456  printError(err);
457  return err;
458 }
459 
461 {
462  return default_ranges.size();
463 }
464 
466 {
468 }
469 
471 {
472  return default_units.size();
473 }
474 
476  DAQ::index_t /*index*/) const
477 {
478  return 0;
479 }
480 
482  DAQ::index_t index,
483  DAQ::index_t /*range*/) const
484 {
485  if (type == DAQ::ChannelType::DI || type == DAQ::ChannelType::DO) {
486  return "";
487  }
488  const std::string formatting = "{:.1f}";
489  auto [min, max] = default_ranges.at(
490  physical_channels_registry.at(type).at(index).range_index);
491  return fmt::format(formatting, min) + std::string(" to ")
492  + fmt::format(formatting, max);
493 }
494 
496  DAQ::index_t index,
497  DAQ::index_t /*reference*/) const
498 {
499  if (type == DAQ::ChannelType::DI || type == DAQ::ChannelType::DO) {
500  return "";
501  }
502  const int32_t ref = physical_channels_registry.at(type).at(index).reference;
503  std::string refstr;
504  switch (ref) {
505  case DAQmx_Val_RSE:
506  refstr = "Ground";
507  break;
508  case DAQmx_Val_NRSE:
509  refstr = "Common";
510  break;
511  case DAQmx_Val_Diff:
512  refstr = "Differential";
513  break;
514  case DAQmx_Val_PseudoDiff:
515  refstr = "Other";
516  break;
517  default:
518  refstr = "Unknown";
519  break;
520  }
521  return refstr;
522 }
523 
525  DAQ::index_t index,
526  DAQ::index_t /*units*/) const
527 {
528  if (type == DAQ::ChannelType::DI || type == DAQ::ChannelType::DO) {
529  return "";
530  }
531  return default_units.at(
532  physical_channels_registry.at(type).at(index).units_index);
533 }
534 
536  DAQ::index_t index) const
537 {
538  if (type == DAQ::ChannelType::DI || type == DAQ::ChannelType::DO) {
539  return 1.0;
540  }
541  return physical_channels_registry.at(type).at(index).gain;
542 }
543 
545  DAQ::index_t index) const
546 {
547  if (type == DAQ::ChannelType::DI || type == DAQ::ChannelType::DO) {
548  return 1.0;
549  }
550  return physical_channels_registry.at(type).at(index).offset;
551 }
552 
554  DAQ::index_t index) const
555 {
556  if (type == DAQ::ChannelType::DI || type == DAQ::ChannelType::DO) {
557  return 0;
558  }
559  return physical_channels_registry.at(type).at(index).range_index;
560 }
561 
563  DAQ::index_t index) const
564 {
565  if (type == DAQ::ChannelType::DI || type == DAQ::ChannelType::DO) {
566  return 0;
567  }
568  return static_cast<size_t>(
569  physical_channels_registry.at(type).at(index).reference);
570 }
571 
573  DAQ::index_t index) const
574 {
575  if (type == DAQ::ChannelType::DI || type == DAQ::ChannelType::DO) {
576  return 0;
577  }
578  return physical_channels_registry.at(type).at(index).units_index;
579 }
580 
582  DAQ::index_t index) const
583 {
584  if (type == DAQ::ChannelType::DI || type == DAQ::ChannelType::DO) {
585  return 0;
586  }
587  return physical_channels_registry.at(type).at(index).units_index;
588 }
589 
591  DAQ::index_t index,
592  double gain)
593 {
594  if (type == DAQ::ChannelType::DI || type == DAQ::ChannelType::DO) {
595  return -1;
596  }
597  physical_channels_registry.at(type).at(index).gain = gain;
598  return 0;
599 }
600 
602  DAQ::index_t index,
603  DAQ::index_t range)
604 {
605  if (type == DAQ::ChannelType::DI || type == DAQ::ChannelType::DO) {
606  return -1;
607  }
608  physical_channels_registry.at(type).at(index).range_index = range;
609  return 0;
610 }
611 
613  DAQ::index_t index,
614  double offset)
615 {
616  if (type == DAQ::ChannelType::DI || type == DAQ::ChannelType::DO) {
617  return -1;
618  }
619  physical_channels_registry.at(type).at(index).offset = offset;
620  return 0;
621 }
622 
624  DAQ::index_t index,
625  DAQ::index_t reference)
626 {
627  if (type == DAQ::ChannelType::DI || type == DAQ::ChannelType::DO) {
628  return -1;
629  }
630  physical_channels_registry.at(type).at(index).range_index = reference;
631  return 0;
632 }
633 
635  DAQ::index_t index,
636  DAQ::index_t units)
637 {
638  if (type == DAQ::ChannelType::DI || type == DAQ::ChannelType::DO) {
639  return -1;
640  }
641  physical_channels_registry.at(type).at(index).units_index = units;
642  return 0;
643 }
644 
646  DAQ::index_t /*index*/,
647  DAQ::index_t /*units*/)
648 {
649  return 0;
650 }
651 
653  DAQ::index_t /*index*/,
654  size_t /*downsample*/)
655 {
656  return 0;
657 }
658 
660  DAQ::index_t /*index*/)
661 {
662  return 0;
663 }
665  DAQ::index_t /*index*/,
666  double /*value*/)
667 {
668  return 0;
669 }
671  DAQ::index_t /*index*/) const
672 {
673  return 0.0;
674 }
676  DAQ::index_t /*index*/,
677  bool /*state*/)
678 {
679  return 0;
680 }
682  DAQ::index_t /*index*/) const
683 {
684  return false;
685 }
687  DAQ::index_t /*index*/) const
688 {
689  return false;
690 }
691 
693  DAQ::direction_t /*direction*/)
694 {
695  return 0;
696 }
697 
699 {
700  int samples_read = 0;
701  size_t value_index = 0;
702  if (!this->active_channels.at(DAQ::ChannelType::AI).empty()) {
703  DAQmxReadAnalogF64(task_list[DAQ::ChannelType::AI],
704  DAQmx_Val_Auto,
705  DAQmx_Val_WaitInfinitely,
706  DAQmx_Val_GroupByScanNumber,
707  std::get<DAQ::ChannelType::AI>(buffer_arrays).data(),
708  std::get<DAQ::ChannelType::AI>(buffer_arrays).size(),
709  &samples_read,
710  nullptr);
711  for (auto& chan : this->active_channels.at(DAQ::ChannelType::AI)) {
712  writeoutput(
713  chan->id,
714  std::get<DAQ::ChannelType::AI>(buffer_arrays).at(value_index));
715  ++value_index;
716  }
717  }
718  samples_read = 0;
719  value_index = 0;
720  int32_t num_bytes_per_sample = 0;
721  if (!this->active_channels.at(DAQ::ChannelType::DI).empty()) {
722  DAQmxReadDigitalLines(task_list[DAQ::ChannelType::DI],
723  DAQmx_Val_Auto,
724  DAQmx_Val_WaitInfinitely,
725  DAQmx_Val_GroupByScanNumber,
726  std::get<DAQ::ChannelType::DI>(buffer_arrays).data(),
727  std::get<DAQ::ChannelType::DI>(buffer_arrays).size(),
728  &samples_read,
729  &num_bytes_per_sample,
730  nullptr);
731  for (auto& chan : this->active_channels.at(DAQ::ChannelType::DI)) {
732  writeoutput(
733  chan->id,
734  std::get<DAQ::ChannelType::DI>(buffer_arrays).at(value_index));
735  ++value_index;
736  }
737  }
738 }
739 
741 {
742  size_t samples_to_write = 0;
743  int samples_written = 0;
744  if (!this->active_channels.at(DAQ::ChannelType::AO).empty()) {
745  for (auto& chan : this->active_channels.at(DAQ::ChannelType::AO)) {
746  std::get<DAQ::ChannelType::AO>(buffer_arrays).at(samples_to_write) =
747  readinput(chan->id);
748  ++samples_to_write;
749  }
750  DAQmxWriteAnalogF64(task_list[DAQ::ChannelType::AO],
751  1,
752  0U,
753  DAQmx_Val_WaitInfinitely,
754  DAQmx_Val_GroupByScanNumber,
755  std::get<DAQ::ChannelType::AO>(buffer_arrays).data(),
756  &samples_written,
757  nullptr);
758  }
759  samples_to_write = 0;
760  if (!this->active_channels.at(DAQ::ChannelType::DO).empty()) {
761  bool digital_value_changed = false;
762  uint8_t old_value = 0;
763  uint8_t new_value = 0;
764  for (auto& chan : this->active_channels.at(DAQ::ChannelType::DO)) {
765  old_value =
766  std::get<DAQ::ChannelType::DO>(buffer_arrays).at(samples_to_write);
767  new_value = static_cast<uint8_t>(readinput(chan->id));
768  if (old_value != new_value) {
769  digital_value_changed = true;
770  }
771  std::get<DAQ::ChannelType::DO>(buffer_arrays).at(samples_to_write) =
772  new_value;
773  ++samples_to_write;
774  }
775  if (digital_value_changed) {
776  DAQmxWriteDigitalLines(
777  task_list[DAQ::ChannelType::DO],
778  1,
779  0U,
780  DAQmx_Val_WaitInfinitely,
781  DAQmx_Val_GroupByScanNumber,
782  std::get<DAQ::ChannelType::DO>(buffer_arrays).data(),
783  &samples_written,
784  nullptr);
785  }
786  }
787 }
788 
789 Driver::Driver()
790  : DAQ::Driver(std::string(DEFAULT_DRIVER_NAME))
791 {
792  this->loadDevices();
793 }
794 
796 {
797  const int32_t device_names_buffer_size = DAQmxGetSysDevNames(nullptr, 0);
798  if (device_names_buffer_size < 0) {
799  printError(device_names_buffer_size);
800  return;
801  }
802  const std::string alpha = "abcdefghijklmnopqrstuvwxyz";
803  std::string string_buffer(static_cast<size_t>(device_names_buffer_size),
804  '\0');
805  DAQmxGetSysDevNames(string_buffer.data(),
806  static_cast<uint32_t>(string_buffer.size()));
807  const std::vector<std::string> device_names =
808  split_string(string_buffer, ", ");
809  std::vector<IO::channel_t> channels;
810  std::vector<std::string> split_channel_names;
811  size_t pos = 0;
812  std::string physical_daq_name;
813  std::string description;
814  int channel_id = 0;
815  for (const auto& internal_dev_name : device_names) {
816  for (size_t query_indx = 0; query_indx < 4; query_indx++) {
817  split_channel_names = physical_channel_names(
818  internal_dev_name, static_cast<DAQ::ChannelType::type_t>(query_indx));
819  physical_daq_name = physical_card_name(internal_dev_name);
820  for (const auto& chan_name : split_channel_names) {
821  description = std::string(query_indx < 2 ? "Analog" : "Digital");
822  description += " ";
823  description += std::string(query_indx % 2 == 0 ? "Output" : "Input");
824  description += " ";
825  pos = chan_name.find_last_of(alpha) + 1;
826  channel_id = std::stoi(chan_name.substr(pos, chan_name.size() - pos));
827  description += std::to_string(channel_id);
828  channels.push_back({chan_name,
829  description,
830  query_indx % 2 == 0 ? IO::OUTPUT : IO::INPUT});
831  }
832  }
833  this->nidaq_devices.emplace_back(
834  physical_daq_name, channels, internal_dev_name);
835  }
836 }
837 
839 
840 std::vector<DAQ::Device*> Driver::getDevices()
841 {
842  std::vector<DAQ::Device*> devices;
843  devices.reserve(this->nidaq_devices.size());
844  for (auto& device : nidaq_devices) {
845  devices.push_back(&device);
846  }
847  return devices;
848 }
849 
850 namespace
851 {
852 Driver* instance = nullptr;
853 } // namespace
854 
856 {
857  if (instance == nullptr) {
858  instance = new Driver();
859  }
860  return instance;
861 }
862 
863 extern "C"
864 {
866 {
867  return Driver::getInstance();
868 }
869 
871 {
872  delete instance;
873 }
874 }
DAQ::index_t getAnalogOffsetUnits(DAQ::ChannelType::type_t type, DAQ::index_t index) const final
int setAnalogRange(DAQ::ChannelType::type_t type, DAQ::index_t index, DAQ::index_t range) final
Device(const Device &)=default
Device & operator=(const Device &)=delete
DAQ::index_t getAnalogUnits(DAQ::ChannelType::type_t type, DAQ::index_t index) const final
double getAnalogGain(DAQ::ChannelType::type_t type, DAQ::index_t index) const final
std::string getAnalogUnitsString(DAQ::ChannelType::type_t type, DAQ::index_t index, DAQ::index_t units) const final
void read() final
size_t getChannelCount(DAQ::ChannelType::type_t type) const final
bool getAnalogCalibrationActive(DAQ::ChannelType::type_t type, DAQ::index_t index) const final
Device(Device &&)=delete
size_t getAnalogDownsample(DAQ::ChannelType::type_t type, DAQ::index_t index) const final
~Device() final
int setAnalogCalibrationActive(DAQ::ChannelType::type_t type, DAQ::index_t index, bool state) final
bool getAnalogCalibrationState(DAQ::ChannelType::type_t type, DAQ::index_t index) const final
int setAnalogZeroOffset(DAQ::ChannelType::type_t type, DAQ::index_t index, double offset) final
size_t getAnalogReferenceCount(DAQ::index_t index) const final
void write() final
double getAnalogCalibrationValue(DAQ::ChannelType::type_t type, DAQ::index_t index) const final
int setChannelActive(DAQ::ChannelType::type_t type, DAQ::index_t index, bool state) final
int setDigitalDirection(DAQ::index_t index, DAQ::direction_t direction) final
int setAnalogDownsample(DAQ::ChannelType::type_t type, DAQ::index_t index, size_t downsample) final
int setAnalogGain(DAQ::ChannelType::type_t type, DAQ::index_t index, double gain) final
int setAnalogCounter(DAQ::ChannelType::type_t type, DAQ::index_t index) final
std::string getAnalogReferenceString(DAQ::ChannelType::type_t type, DAQ::index_t index, DAQ::index_t reference) const final
Device & operator=(Device &&)=delete
int setAnalogCalibrationValue(DAQ::ChannelType::type_t type, DAQ::index_t index, double value) final
int setAnalogReference(DAQ::ChannelType::type_t type, DAQ::index_t index, DAQ::index_t reference) final
double getAnalogZeroOffset(DAQ::ChannelType::type_t type, DAQ::index_t index) const final
size_t getAnalogRangeCount(DAQ::index_t index) const final
size_t getAnalogUnitsCount(DAQ::index_t index) const final
DAQ::index_t getAnalogReference(DAQ::ChannelType::type_t type, DAQ::index_t index) const final
bool getChannelActive(DAQ::ChannelType::type_t type, DAQ::index_t index) const final
int setAnalogUnits(DAQ::ChannelType::type_t type, DAQ::index_t index, DAQ::index_t units) final
DAQ::index_t getAnalogRange(DAQ::ChannelType::type_t type, DAQ::index_t index) const final
std::string getAnalogRangeString(DAQ::ChannelType::type_t type, DAQ::index_t index, DAQ::index_t range) const final
int setAnalogOffsetUnits(DAQ::ChannelType::type_t type, DAQ::index_t index, DAQ::index_t units) final
void unloadDevices() final
std::vector< DAQ::Device * > getDevices() final
void loadDevices() final
static DAQ::Driver * getInstance()
double & readinput(size_t index)
Definition: io.cpp:70
void writeoutput(size_t index, const double &data)
Definition: io.cpp:80
void setActive(bool act)
Definition: io.hpp:188
void ERROR_MSG(const std::string &errmsg, Args... args)
Definition: debug.hpp:36
std::string type2string(type_t type)
Definition: daq.hpp:55
DAQ Oriented Classes.
Definition: daq.hpp:37
uint64_t index_t
Definition: daq.hpp:82
std::array< std::string, 2 > get_default_units()
Definition: daq.hpp:122
std::array< analog_range_t, 7 > get_default_ranges()
Definition: daq.hpp:110
direction_t
Definition: daq.hpp:93
@ INPUT
Definition: io.hpp:54
@ OUTPUT
Definition: io.hpp:53
void printError(int32_t status)
const std::string_view DEFAULT_DRIVER_NAME
DAQ::Driver * getRTXIDAQDriver()
void deleteRTXIDAQDriver()
std::string physical_card_name(const std::string &device_name)
std::vector< std::string > split_string(const std::string &buffer, const std::string &delim)
std::vector< std::string > physical_channel_names(const std::string &device, DAQ::ChannelType::type_t chan_type)
int32_t addToTask(TaskHandle task_handle) const
DAQ::ChannelType::type_t type
physical_channel_t(std::string chan_name, DAQ::ChannelType::type_t chan_type, size_t chan_id)