32 #define QFileExistsEvent (QEvent::User+0) 33 #define QSetFileNameEditEvent (QEvent::User+1) 34 #define QDisableGroupsEvent (QEvent::User+2) 35 #define QEnableGroupsEvent (QEvent::User+3) 48 static int eventEnumIndex = QEvent::staticMetaObject.indexOfEnumerator(
"Type");
52 QString name = QEvent::staticMetaObject.enumerator(eventEnumIndex).valueToKey(ev->type());
60 return str.maybeSpace();
69 static void findDAQDevice(
DAQ::Device *dev,
void *arg)
79 void buildBlockPtrList(
IO::Block *block,
void *arg)
81 std::vector<IO::Block *> *list =
reinterpret_cast<std::vector<IO::Block *> *
> (arg);
82 list->push_back(block);
85 struct FileExistsEventData
92 struct SetFileNameEditEventData
98 class InsertChannelEvent:
public RT::Event 103 ~InsertChannelEvent(
void);
113 class RemoveChannelEvent:
public RT::Event 118 ~RemoveChannelEvent(
void);
131 ~OpenFileEvent(
void);
139 class StartRecordingEvent:
public RT::Event 143 ~StartRecordingEvent(
void);
151 class StopRecordingEvent:
public RT::Event 155 ~StopRecordingEvent(
void);
166 AsyncDataEvent(
const double *,
size_t,
AtomicFifo &);
167 ~AsyncDataEvent(
void);
190 recording(r), channels(l), end(e), channel(c)
194 InsertChannelEvent::~InsertChannelEvent(
void)
198 int InsertChannelEvent::callback(
void)
202 channels.insertRT(end, channel);
208 recording(r), channels(l), channel(c)
212 RemoveChannelEvent::~RemoveChannelEvent(
void)
216 int RemoveChannelEvent::callback(
void)
220 channels.removeRT(channel);
224 OpenFileEvent::OpenFileEvent(QString &n,
AtomicFifo &f) :
229 OpenFileEvent::~OpenFileEvent(
void)
233 int OpenFileEvent::callback(
void)
237 token.
size = filename.length() + 1;
239 fifo.write(&token,
sizeof(token));
240 fifo.write(filename.toLatin1().constData(), token.
size);
244 StartRecordingEvent::StartRecordingEvent(
bool &r,
AtomicFifo &f) :
245 recording(r), fifo(f)
249 StartRecordingEvent::~StartRecordingEvent(
void)
253 int StartRecordingEvent::callback(
void)
260 fifo.write(&token,
sizeof(token));
264 StopRecordingEvent::StopRecordingEvent(
bool &r,
AtomicFifo &f) :
265 recording(r), fifo(f)
269 StopRecordingEvent::~StopRecordingEvent(
void)
273 int StopRecordingEvent::callback(
void)
280 fifo.write(&token,
sizeof(token));
284 AsyncDataEvent::AsyncDataEvent(
const double *d,
size_t s,
AtomicFifo &f) :
285 data(d), size(s), fifo(f)
289 AsyncDataEvent::~AsyncDataEvent(
void)
293 int AsyncDataEvent::callback(
void)
297 token.
size = size *
sizeof(double);
299 fifo.write(&token,
sizeof(token));
300 fifo.write(data, token.
size);
309 DoneEvent::~DoneEvent(
void)
313 int DoneEvent::callback(
void)
319 fifo.write(&token,
sizeof(token));
359 event.setParam(
"filename",
const_cast<char *
> (filename.toLatin1().constData()));
369 event.setParam(
"data",
const_cast<double *
> (data));
370 event.setParam(
"size", &size);
377 DataRecorder::Channel::Channel(
void)
381 DataRecorder::Channel::~Channel(
void)
386 QWidget(parent),
RT::Thread(
RT::Thread::MinimumPriority), fifo(buffersize), recording(false)
389 "<p><b>Data Recorder:</b><br>The Data Recorder writes data to an HDF5 file format " 390 "All available signals for saving to file are automatically detected. Currently " 391 "loaded user modules are listed in the \"Block\" drop-down box. Available DAQ cards " 392 "are listed here as /proc/analogy/devices. Use the \"Type\" and \"Channel\" drop-down boxes " 393 "to select the signals that you want to save. Use the left and right arrow buttons to " 394 "add these signals to the file. You may select a downsampling rate that is applied " 395 "to the real-time period for execution (set in the System Control Panel). The real-time " 396 "period and the data downsampling rate are both saved as metadata in the HDF5 file " 397 "so that you can reconstruct your data correctly. The current recording status of " 398 "the Data Recorder is shown at the bottom.</p>");
401 subWindow =
new QMdiSubWindow;
402 subWindow->setWindowIcon(QIcon(
"/usr/local/share/rtxi/RTXI-widget-icon.png"));
403 subWindow->setAttribute(Qt::WA_DeleteOnClose);
404 subWindow->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint |
405 Qt::WindowMinimizeButtonHint);
409 QGridLayout *layout =
new QGridLayout;
412 channelGroup =
new QGroupBox(tr(
"Channel Selection"));
413 QVBoxLayout *channelLayout =
new QVBoxLayout;
416 channelLayout->addWidget(
new QLabel(tr(
"Block:")));
417 blockList =
new QComboBox;
418 channelLayout->addWidget(blockList);
419 QObject::connect(blockList,SIGNAL(activated(
int)),
this, SLOT(buildChannelList(
void)));
421 channelLayout->addWidget(
new QLabel(tr(
"Type:")));
422 typeList =
new QComboBox;
423 channelLayout->addWidget(typeList);
424 typeList->addItem(
"Input");
425 typeList->addItem(
"Output");
426 typeList->addItem(
"Parameter");
427 typeList->addItem(
"State");
428 typeList->addItem(
"Event");
429 QObject::connect(typeList,SIGNAL(activated(
int)),
this,SLOT(buildChannelList(
void)));
431 channelLayout->addWidget(
new QLabel(tr(
"Channel:")));
432 channelList =
new QComboBox;
433 channelLayout->addWidget(channelList);
436 channelGroup->setLayout(channelLayout);
439 rButton =
new QPushButton(
"Add");
440 channelLayout->addWidget(rButton);
441 QObject::connect(rButton,SIGNAL(released(
void)),
this,SLOT(insertChannel(
void)));
442 rButton->setEnabled(
false);
443 lButton =
new QPushButton(
"Remove");
444 channelLayout->addWidget(lButton);
445 QObject::connect(lButton,SIGNAL(released(
void)),
this,SLOT(removeChannel(
void)));
446 lButton->setEnabled(
false);
449 stampGroup =
new QGroupBox(tr(
"Tag Data"));
450 QHBoxLayout *stampLayout =
new QHBoxLayout;
453 timeStampEdit =
new QLineEdit;
454 stampLayout->addWidget(timeStampEdit);
455 addTag =
new QPushButton(tr(
"Tag"));
456 stampLayout->addWidget(addTag);
457 QObject::connect(addTag,SIGNAL(released(
void)),
this,SLOT(addNewTag(
void)));
460 stampGroup->setLayout(stampLayout);
463 sampleGroup =
new QGroupBox(tr(
"Trial Metadata"));
464 QHBoxLayout *sampleLayout =
new QHBoxLayout;
467 trialNumLbl =
new QLabel;
468 trialNumLbl->setText(
"Trial #:");
469 trialNumLbl->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
470 sampleLayout->addWidget(trialNumLbl);
471 trialNum =
new QLabel;
472 trialNum->setText(
"0");
473 trialNum->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
474 sampleLayout->addWidget(trialNum);
476 trialLengthLbl =
new QLabel;
477 trialLengthLbl->setText(
"Trial Length (s):");
478 sampleLayout->addWidget(trialLengthLbl);
479 trialLength =
new QLabel;
480 trialLength->setText(
"No data recorded");
481 trialLength->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
482 sampleLayout->addWidget(trialLength);
484 fileSizeLbl =
new QLabel;
485 fileSizeLbl->setText(
"File Size (MB):");
486 sampleLayout->addWidget(fileSizeLbl);
487 fileSize =
new QLabel;
488 fileSize->setText(
"No data recorded");
489 sampleLayout->addWidget(fileSize);
490 fileSize->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
493 sampleGroup->setLayout(sampleLayout);
496 fileGroup =
new QGroupBox(tr(
"File Control"));
497 QHBoxLayout *fileLayout =
new QHBoxLayout;
500 fileLayout->addWidget(
new QLabel(tr(
"File Name:")));
501 fileNameEdit =
new QLineEdit;
502 fileNameEdit->setReadOnly(
true);
503 fileLayout->addWidget(fileNameEdit);
504 QPushButton *fileChangeButton =
new QPushButton(
"Choose File");
505 fileLayout->addWidget(fileChangeButton);
506 QObject::connect(fileChangeButton,SIGNAL(released(
void)),
this,SLOT(changeDataFile(
void)));
508 fileLayout->addWidget(
new QLabel(tr(
"Downsample \nRate:")));
509 downsampleSpin =
new QSpinBox(
this);
510 downsampleSpin->setMinimum(1);
511 downsampleSpin->setMaximum(500);
512 fileLayout->addWidget(downsampleSpin);
513 QObject::connect(downsampleSpin,SIGNAL(valueChanged(
int)),
this,SLOT(
updateDownsampleRate(
int)));
516 fileGroup->setLayout(fileLayout);
519 listGroup =
new QGroupBox(tr(
"Currently Recording"));
520 QGridLayout *listLayout =
new QGridLayout;
523 selectionBox =
new QListWidget;
524 listLayout->addWidget(selectionBox,1,1,4,5);
527 listGroup->setLayout(listLayout);
530 buttonGroup =
new QGroupBox;
531 QHBoxLayout *buttonLayout =
new QHBoxLayout;
534 startRecordButton =
new QPushButton(
"Start Recording");
535 QObject::connect(startRecordButton,SIGNAL(released(
void)),
this,SLOT(
startRecordClicked(
void)));
536 buttonLayout->addWidget(startRecordButton);
537 startRecordButton->setEnabled(
false);
538 stopRecordButton =
new QPushButton(
"Stop Recording");
539 QObject::connect(stopRecordButton,SIGNAL(released(
void)),
this,SLOT(
stopRecordClicked(
void)));
540 buttonLayout->addWidget(stopRecordButton);
541 stopRecordButton->setEnabled(
false);
542 closeButton =
new QPushButton(
"Close");
543 QObject::connect(closeButton,SIGNAL(released(
void)),subWindow,SLOT(close()));
544 buttonLayout->addWidget(closeButton);
545 recordStatus =
new QLabel;
546 buttonLayout->addWidget(recordStatus);
547 recordStatus->setText(
"Not ready.");
548 recordStatus->setFrameStyle(QFrame::Panel | QFrame::Sunken);
549 recordStatus->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
552 buttonGroup->setLayout(buttonLayout);
555 layout->addWidget(channelGroup, 0, 0, 1, 2);
556 layout->addWidget(listGroup, 0, 2, 1, 4);
557 layout->addWidget(stampGroup, 2, 0, 2, 6);
558 layout->addWidget(fileGroup, 4, 0, 1, 6);
559 layout->addWidget(sampleGroup, 5, 0, 1, 6);
560 layout->addWidget(buttonGroup, 6, 0, 1, 6);
563 setWindowTitle(QString::number(
getID()) +
" Data Recorder");
566 subWindow->setWidget(
this);
567 subWindow->setFixedSize(subWindow->minimumSizeHint());
578 for (std::vector<IO::Block *>::const_iterator i = blockPtrList.begin(), end = blockPtrList.end(); i != end; ++i)
579 blockList->addItem(QString::fromStdString((*i)->getName()) +
" " + QString::number((*i)->getID()));
587 ERROR_MSG(
"DataRecorder::Panel: WARNING: Atomic FIFO is not lock free\n");
593 pthread_create(&thread, 0, bounce,
this);
606 DoneEvent RTevent(fifo);
608 pthread_join(thread, 0);
616 if (recording && !counter++)
619 double data[channels.size()];
623 token.
size = channels.size() *
sizeof(double);
626 data[n++] = i->block->getValue(i->type, i->index);
628 fifo.write(&token,
sizeof(token));
629 fifo.write(data,
sizeof(data));
632 counter %= downsample_rate;
641 blockPtrList.push_back(block);
642 blockList->addItem(QString::fromStdString(block->
getName()) +
" " + QString::number(block->
getID()));
648 QString name = QString::fromStdString(block->
getName()) +
" " + QString::number(block->
getID());
650 for (; n < blockList->count() && blockList->itemText(n) != name; ++n) ;
651 if (n < blockList->count())
652 blockList->removeItem(n);
653 blockPtrList.erase(blockPtrList.begin() + n);
656 if (i->block == block) {
657 if (recording) i->block = 0;
658 RemoveChannelEvent RTevent(recording, channels, *i);
660 QList<QListWidgetItem*> channelItems = selectionBox->findItems(i->name, Qt::MatchExactly);
661 if (!channelItems.isEmpty()) {
663 selectionBox->takeItem(selectionBox->row(channelItems.takeFirst()));
671 QString filename(
reinterpret_cast<char*
> (event->
getParam(
"filename")));
672 OpenFileEvent RTevent(filename, fifo);
677 StartRecordingEvent RTevent(recording, fifo);
682 StopRecordingEvent RTevent(recording, fifo);
687 AsyncDataEvent RTevent(
reinterpret_cast<double *
> (event->
getParam(
"data")),*
reinterpret_cast<size_t *
> (event->
getParam(
"size")), fifo);
702 QString filename = QString(
reinterpret_cast<char*
> (event->
getParam(
"filename")));
705 token.
size = filename.length() + 1;
707 fifo.write(&token,
sizeof(token));
708 fifo.write(filename.toLatin1().constData(), token.
size);
717 fifo.write(&token,
sizeof(token));
726 fifo.write(&token,
sizeof(token));
730 size_t size = *
reinterpret_cast<size_t *
> (
event->getParam(
"size"));
733 token.
size = size *
sizeof(double);
735 fifo.write(&token,
sizeof(token));
746 data.
index =
reinterpret_cast<size_t> (
event->getParam(
"index"));
747 data.
step = file.idx;
748 data.
value = *
reinterpret_cast<double *
> (
event->getParam(
"value"));
749 fifo.write(&token,
sizeof(token));
750 fifo.write(&data,
sizeof(data));
759 void DataRecorder::Panel::buildChannelList(
void)
761 channelList->clear();
762 if (!blockList->count())
766 IO::Block *block = blockPtrList[blockList->currentIndex()];
770 switch (typeList->currentIndex())
773 type = Workspace::INPUT;
776 type = Workspace::OUTPUT;
779 type = Workspace::PARAMETER;
782 type = Workspace::STATE;
785 type = Workspace::EVENT;
788 ERROR_MSG(
"DataRecorder::Panel::buildChannelList : invalid type selection\n");
789 typeList->setCurrentIndex(0);
790 type = Workspace::INPUT;
793 for (
size_t i = 0; i < block->
getCount(type); ++i)
794 channelList->addItem(QString::fromStdString(block->
getName(type, i)));
796 if(channelList->count())
797 rButton->setEnabled(
true);
799 rButton->setEnabled(
false);
803 void DataRecorder::Panel::changeDataFile(
void)
805 QFileDialog fileDialog(
this);
806 fileDialog.setFileMode(QFileDialog::AnyFile);
807 fileDialog.setWindowTitle(
"Select Data File");
810 userprefs.setPath(QSettings::NativeFormat, QSettings::SystemScope,
"/usr/local/share/rtxi/");
811 fileDialog.setDirectory(userprefs.value(
"/dirs/data", getenv(
"HOME")).toString());
813 QStringList filterList;
814 filterList.push_back(
"HDF5 files (*.h5)");
815 filterList.push_back(
"All files (*.*)");
816 fileDialog.setNameFilters(filterList);
817 fileDialog.selectNameFilter(
"HDF5 files (*.h5)");
820 if(fileDialog.exec())
821 files = fileDialog.selectedFiles();
824 if(files.isEmpty() || files[0] == NULL || files[0] ==
"/" )
829 if (!filename.toLower().endsWith(QString(
".h5")))
833 userprefs.setValue(
"/dirs/data", fileDialog.directory().path());
836 OpenFileEvent RTevent(filename, fifo);
841 void DataRecorder::Panel::insertChannel(
void)
843 if (!blockList->count() || !channelList->count())
846 Channel *channel =
new Channel();
847 channel->block = blockPtrList[blockList->currentIndex()];
848 switch (typeList->currentIndex())
851 channel->type = Workspace::INPUT;
854 channel->type = Workspace::OUTPUT;
857 channel->type = Workspace::PARAMETER;
860 channel->type = Workspace::STATE;
863 channel->type = Workspace::EVENT;
866 ERROR_MSG(
"DataRecorder::Panel::insertChannel : invalid type selection\n");
867 typeList->setCurrentIndex(0);
868 channel->type = Workspace::INPUT;
870 channel->index = channelList->currentIndex();
872 channel->name.sprintf(
"%s %ld : %s", channel->block->getName().c_str(),
873 channel->block->getID(), channel->block->getName(channel->type, channel->index).c_str());
875 if(selectionBox->findItems(QString(channel->name), Qt::MatchExactly).isEmpty())
877 InsertChannelEvent RTevent(recording, channels, channels.end(), *channel);
879 selectionBox->addItem(channel->name);
882 if(selectionBox->count())
884 lButton->setEnabled(
true);
885 if(!fileNameEdit->text().isEmpty())
887 startRecordButton->setEnabled(
true);
892 startRecordButton->setEnabled(
false);
893 lButton->setEnabled(
false);
898 void DataRecorder::Panel::removeChannel(
void)
900 if(!selectionBox->count() || selectionBox->selectedItems().isEmpty())
904 if (i->name == selectionBox->selectedItems().first()->text())
906 RemoveChannelEvent RTevent(recording, channels, *i);
908 selectionBox->takeItem(selectionBox->row(selectionBox->selectedItems().first()));
912 if(selectionBox->count())
914 startRecordButton->setEnabled(
true);
915 lButton->setEnabled(
true);
919 startRecordButton->setEnabled(
false);
920 lButton->setEnabled(
false);
925 void DataRecorder::Panel::addNewTag(
void)
929 newTag += timeStampEdit->text().toStdString();
930 dataTags.push_back(newTag);
931 timeStampEdit->clear();
932 recordStatus->setText(
"Tagged");
938 if(fileNameEdit->text().isEmpty())
940 QMessageBox::critical(
941 this,
"Data file not specified.",
942 "Please specify a file to write data to.",
943 QMessageBox::Ok, QMessageBox::NoButton);
947 StartRecordingEvent RTevent(recording, fifo);
954 StopRecordingEvent RTevent(recording, fifo);
971 FileExistsEventData *data =
reinterpret_cast<FileExistsEventData *
> (
event->getData());
972 data->response = QMessageBox::question(
this,
"File exists",
973 "The file already exists. What would you like to do?",
974 "Append",
"Overwrite",
"Cancel", 0, 2);
975 recordStatus->setText(
"Not Recording");
976 data->done.wakeAll();
983 SetFileNameEditEventData *data =
reinterpret_cast<SetFileNameEditEventData *
> (
event->getData());
984 fileNameEdit->setText(data->filename);
985 recordStatus->setText(
"Ready.");
986 if(selectionBox->count())
988 startRecordButton->setEnabled(
true);
990 data->done.wakeAll();
995 startRecordButton->setEnabled(
false);
996 stopRecordButton->setEnabled(
true);
997 closeButton->setEnabled(
false);
998 channelGroup->setEnabled(
false);
999 sampleGroup->setEnabled(
false);
1000 recordStatus->setText(
"Recording...");
1004 startRecordButton->setEnabled(
true);
1005 stopRecordButton->setEnabled(
false);
1006 closeButton->setEnabled(
true);
1007 channelGroup->setEnabled(
true);
1008 sampleGroup->setEnabled(
true);
1009 recordStatus->setText(
"Ready.");
1010 fileSize->setNum(
int(QFile(fileNameEdit->text()).size())/1024.0/1024.0);
1018 for (
int i = 0; i < s.
loadInteger(
"Num Channels"); ++i)
1022 std::ostringstream str;
1030 channel->block = block;
1031 channel->type = s.
loadInteger(str.str() +
" type");
1032 channel->index = s.
loadInteger(str.str() +
" index");
1033 channel->name.sprintf(
"%s %ld : %s", channel->block->
getName().c_str(),
1034 channel->block->
getID(), channel->block->
getName(channel->type, channel->index).c_str());
1036 channels.insert(channels.end(), *channel);
1037 selectionBox->addItem(channel->name);
1038 if(selectionBox->count())
1039 lButton->setEnabled(
true);
1050 downsampleSpin->setValue(s.
loadInteger(
"Downsample"));
1058 else if (isMinimized())
1061 QPoint pos = parentWidget()->pos();
1065 s.
saveInteger(
"Downsample", downsampleSpin->value());
1070 std::ostringstream str;
1073 s.
saveInteger(str.str() +
" ID", i->block->getID());
1079 void *DataRecorder::Panel::bounce(
void *param)
1081 Panel *that =
reinterpret_cast<Panel *
> (param);
1084 that->processData();
1089 void DataRecorder::Panel::processData(
void)
1093 CLOSED, OPENED, RECORD,
1096 tokenRetrieved =
false;
1102 if(fifo.read(&_token,
sizeof(_token)))
1103 tokenRetrieved =
true;
1107 nanosleep(&sleep, NULL);
1111 if (_token.type ==
SYNC)
1113 if (state == RECORD)
1115 double data[_token.size /
sizeof(double)];
1116 if(!fifo.read(data, _token.size))
1118 H5PTappend(file.cdata, 1, data);
1122 else if (_token.type ==
ASYNC)
1124 if (state == RECORD)
1126 double data[_token.size /
sizeof(double)];
1127 if(!fifo.read(data, _token.size))
1131 hsize_t array_size[] = { _token.size /
sizeof(double) };
1132 hid_t array_space = H5Screate_simple(1, array_size, array_size);
1133 hid_t array_type = H5Tarray_create(H5T_IEEE_F64LE, 1, array_size);
1135 QString data_name = QString::number(
static_cast<unsigned long long> (_token.time));
1136 hid_t adata = H5Dcreate(file.adata, data_name.toLatin1().constData(),
1137 array_type, array_space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1138 H5Dwrite(adata, array_type, H5S_ALL, H5S_ALL, H5P_DEFAULT, data);
1141 H5Tclose(array_type);
1142 H5Sclose(array_space);
1146 else if (_token.type ==
OPEN)
1148 if (state == RECORD)
1150 if (state != CLOSED)
1152 char filename_string[_token.size];
1153 if(!fifo.read(filename_string, _token.size))
1155 QString filename = filename_string;
1161 else if (_token.type ==
CLOSE)
1163 if (state == RECORD)
1165 if (state != CLOSED)
1169 else if (_token.type ==
START)
1171 if (state == OPENED)
1177 QApplication::postEvent(
this, event);
1180 else if (_token.type ==
STOP)
1182 if (state == RECORD)
1188 QApplication::postEvent(
this, event);
1191 else if (_token.type ==
DONE)
1193 if (state == RECORD)
1195 if (state != CLOSED)
1199 else if (_token.type ==
PARAM)
1201 param_change_t data;
1202 if(!fifo.read(&data,
sizeof(data)))
1207 if (block && state == RECORD)
1212 param_type = H5Tcreate(H5T_COMPOUND,
sizeof(
param_hdf_t));
1213 H5Tinsert(param_type,
"index", HOFFSET(
param_hdf_t,index), H5T_STD_I64LE);
1214 H5Tinsert(param_type,
"value", HOFFSET(
param_hdf_t,value), H5T_IEEE_F64LE);
1216 QString parameter_name = QString::number(block->
getID()) +
" " 1217 + QString::fromStdString(block->
getName()) +
" : " 1218 + QString::fromStdString(block->
getName(Workspace::PARAMETER, data.index));
1220 hid_t data = H5PTopen(file.pdata, parameter_name.toLatin1().constData());
1221 H5PTappend(data, 1, ¶m);
1223 H5Tclose(param_type);
1226 tokenRetrieved =
false;
1230 int DataRecorder::Panel::openFile(QString &filename)
1233 if(!pthread_equal(pthread_self(),thread))
1235 std::cout <<
"DataRecorder::Panel::openFile : called by invalid thread\n";
1240 if (QFile::exists(filename))
1243 CustomEvent *
event =
new CustomEvent(
static_cast<QEvent::Type
>QFileExistsEvent);
1244 FileExistsEventData data;
1245 event->setData(
static_cast<void *
>(&data));
1246 data.filename = filename;
1247 QApplication::postEvent(
this, event);
1248 data.done.wait(&mutex);
1251 if (data.response == 0)
1253 file.id = H5Fopen(filename.toLatin1().constData(), H5F_ACC_RDWR, H5P_DEFAULT);
1256 H5Eset_auto(H5E_DEFAULT, NULL, NULL);
1257 for (trial_num = 1;; ++trial_num)
1259 trial_name =
"/Trial" + QString::number(trial_num);
1260 file.trial = H5Gopen(file.id, trial_name.toLatin1().constData(), H5P_DEFAULT);
1263 H5Eclear(H5E_DEFAULT);
1268 H5Gclose(file.trial);
1271 trialNum->setNum(
int(trial_num)-1);
1273 else if (data.response == 1)
1275 file.id = H5Fcreate(filename.toLatin1().constData(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
1276 trialNum->setText(
"0");
1285 file.id = H5Fcreate(filename.toLatin1().constData(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
1286 trialNum->setText(
"0");
1290 H5E_type_t error_type;
1292 error_size = H5Eget_msg(file.id, &error_type, NULL, 0);
1293 char error_msg[error_size + 1];
1294 H5Eget_msg(file.id, &error_type, error_msg, error_size);
1295 error_msg[error_size] = 0;
1298 ERROR_MSG(
"DataRecorder::Panel::processData : failed to open \"%s\" for writing with error : %s\n", filename.toStdString().c_str(),error_msg);
1304 SetFileNameEditEventData data;
1305 data.filename = filename;
1306 event->setData(
static_cast<void*
>(&data));
1307 QApplication::postEvent(
this, event);
1308 data.done.wait(&mutex);
1314 void DataRecorder::Panel::closeFile(
bool shutdown)
1317 if(!pthread_equal(pthread_self(),thread))
1319 ERROR_MSG(
"DataRecorder::Panel::closeFile : called by invalid thread\n");
1324 if(!dataTags.empty())
1327 hid_t tag_type, tag_space, data;
1329 hsize_t dims[1] = {1};
1330 tag_type = H5Tcreate(H5T_STRING,
TAG_SIZE);
1331 tag_space = H5Screate_simple(1, dims, NULL);
1334 file.tdata = H5Gcreate(file.id,
"Tags", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1338 for(std::vector<std::string>::iterator it = dataTags.begin(); it != dataTags.end(); ++it)
1340 data = H5Dcreate(file.tdata, std::string(
"Tag " + std::to_string(i++)).c_str(), tag_type, tag_space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1347 H5Sclose(tag_space);
1349 H5Gclose(file.tdata);
1359 SetFileNameEditEventData data;
1361 event->setData(
static_cast<void*
>(&data));
1362 QApplication::postEvent(
this, event);
1363 data.done.wait(&mutex);
1368 int DataRecorder::Panel::startRecording(
long long timestamp)
1371 if(!pthread_equal(pthread_self(),thread))
1373 ERROR_MSG(
"DataRecorder::Panel::startRecording : called by invalid thread\n");
1381 H5Eset_auto(H5E_DEFAULT, NULL, NULL);
1383 for (trial_num = 1;; ++trial_num)
1385 trial_name =
"/Trial" + QString::number(trial_num);
1386 file.trial = H5Gopen(file.id, trial_name.toLatin1().constData(), H5P_DEFAULT);
1390 H5Eclear(H5E_DEFAULT);
1394 H5Gclose(file.trial);
1397 trialNum->setNum(
int(trial_num));
1398 file.trial = H5Gcreate(file.id, trial_name.toLatin1().constData(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1399 file.pdata = H5Gcreate(file.trial,
"Parameters", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1400 file.adata = H5Gcreate(file.trial,
"Asynchronous Data", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1401 file.sdata = H5Gcreate(file.trial,
"Synchronous Data", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1402 file.sysdata = H5Gcreate(file.trial,
"System Settings", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1404 hid_t scalar_space = H5Screate(H5S_SCALAR);
1405 hid_t string_type = H5Tcopy(H5T_C_S1);
1406 size_t string_size = 1024;
1407 H5Tset_size(string_type, string_size);
1410 data = H5Dcreate(file.trial,
"Version", string_type,
1411 scalar_space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1412 std::string version_string = QString(
VERSION).toStdString();
1413 char * version_c_string =
new char[version_string.length()+1];
1414 std::strcpy(version_c_string, version_string.c_str());
1415 H5Dwrite(data, string_type, H5S_ALL, H5S_ALL, H5P_DEFAULT, version_c_string);
1416 delete[] version_c_string;
1420 data = H5Dcreate(file.trial,
"Period (ns)", H5T_STD_U64LE, scalar_space,
1421 H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1422 H5Dwrite(data, H5T_STD_U64LE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &period);
1425 long long downsample = downsample_rate;
1426 data = H5Dcreate(file.trial,
"Downsampling Rate", H5T_STD_U64LE,
1427 scalar_space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1428 H5Dwrite(data, H5T_STD_U64LE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &downsample);
1431 data = H5Dcreate(file.trial,
"Timestamp Start (ns)", H5T_STD_U64LE,
1432 scalar_space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1433 H5Dwrite(data, H5T_STD_U64LE, H5S_ALL, H5S_ALL, H5P_DEFAULT, ×tamp);
1436 data = H5Dcreate(file.trial,
"Date", string_type,
1437 scalar_space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1438 std::string date_string = QDateTime::currentDateTime().toString(Qt::ISODate).toStdString();
1439 char * date_c_string =
new char[date_string.length()+1];
1440 std::strcpy(date_c_string, date_string.c_str());
1441 H5Dwrite(data, string_type, H5S_ALL, H5S_ALL, H5P_DEFAULT, date_c_string);
1442 delete[] date_c_string;
1446 param_type = H5Tcreate(H5T_COMPOUND,
sizeof(
param_hdf_t));
1447 H5Tinsert(param_type,
"index", HOFFSET(
param_hdf_t,index), H5T_STD_I64LE);
1448 H5Tinsert(param_type,
"value", HOFFSET(
param_hdf_t,value), H5T_IEEE_F64LE);
1453 for (
size_t j = 0; j < block->
getCount(Workspace::PARAMETER); ++j)
1455 QString parameter_name = QString::number(block->
getID()) +
" " 1456 + QString::fromStdString(block->
getName()) +
" : " + QString::fromStdString(block->
getName(Workspace::PARAMETER, j));
1457 data = H5PTcreate_fl(file.pdata, parameter_name.toLatin1().constData(), param_type,
sizeof(
param_hdf_t), -1);
1459 H5PTappend(data, 1, &
value);
1462 for (
size_t j = 0; j < block->
getCount(Workspace::COMMENT); ++j)
1464 QString comment_name = QString::number(block->
getID()) +
" " 1465 + QString::fromStdString(block->
getName()) +
" : " + QString::fromStdString(block->
getName(Workspace::COMMENT, j));
1466 hsize_t dims =
dynamic_cast<Workspace::Instance *
> (block)->getValueString(Workspace::COMMENT, j).size() + 1;
1467 hid_t comment_space = H5Screate_simple(1, &dims, &dims);
1468 data = H5Dcreate(file.pdata, comment_name.toLatin1().constData(), H5T_C_S1, comment_space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1469 H5Dwrite(data, H5T_C_S1, H5S_ALL, H5S_ALL, H5P_DEFAULT,
dynamic_cast<Workspace::Instance *
> (block)->getValueString(Workspace::COMMENT, j).c_str());
1474 H5Tclose(param_type);
1479 std::string rec_chan_name = std::to_string(++count) +
" " + i->name.toStdString();
1480 rec_chan_name.erase(std::remove_if(rec_chan_name.begin(), rec_chan_name.end(), &ispunct), rec_chan_name.end());
1481 hid_t data = H5Dcreate(file.sdata, rec_chan_name.c_str(), string_type, scalar_space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1482 H5Dwrite(data, string_type, H5S_ALL, H5S_ALL, H5P_DEFAULT, rec_chan_name.c_str());
1498 std::string chan_name =
"Analog Channel " + std::to_string(i);
1499 file.chandata = H5Gcreate(file.sysdata, chan_name.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1501 hid_t data = H5Dcreate(file.chandata,
"Range", string_type, scalar_space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1503 char * range_c_string =
new char[range_string.length()+1];
1504 std::strcpy(range_c_string, range_string.c_str());
1505 H5Dwrite(data, string_type, H5S_ALL, H5S_ALL, H5P_DEFAULT, range_c_string);
1506 delete[] range_c_string;
1508 data = H5Dcreate(file.chandata,
"Reference", string_type, scalar_space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1510 char * ref_c_string =
new char[ref_string.length()+1];
1511 std::strcpy(ref_c_string, ref_string.c_str());
1512 H5Dwrite(data, string_type, H5S_ALL, H5S_ALL, H5P_DEFAULT, ref_c_string);
1513 delete[] ref_c_string;
1516 data = H5Dcreate(file.chandata,
"Gain", H5T_IEEE_F64LE, scalar_space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1517 H5Dwrite(data, H5T_IEEE_F64LE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &scale);
1520 data = H5Dcreate(file.chandata,
"Offset", H5T_IEEE_F64LE, scalar_space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1521 H5Dwrite(data, H5T_IEEE_F64LE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &offset);
1524 data = H5Dcreate(file.chandata,
"Downsample", H5T_STD_I16LE, scalar_space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1525 H5Dwrite(data, H5T_STD_I16LE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &downsample);
1529 H5Tclose(string_type);
1530 H5Sclose(scalar_space);
1532 if (channels.size())
1534 hsize_t array_size[] = { channels.size() };
1535 hid_t array_type = H5Tarray_create(H5T_IEEE_F64LE, 1, array_size);
1536 file.cdata = H5PTcreate_fl(file.sdata,
"Channel Data", array_type, (hsize_t) 64, 1);
1537 H5Tclose(array_type);
1545 void DataRecorder::Panel::stopRecording(
long long timestamp)
1548 if(!pthread_equal(pthread_self(),thread))
1550 ERROR_MSG(
"DataRecorder::Panel::stopRecording : called by invalid thread\n");
1556 hid_t scalar_space = H5Screate(H5S_SCALAR);
1557 hid_t data = H5Dcreate(file.trial,
"Timestamp Stop (ns)", H5T_STD_U64LE,
1558 scalar_space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1559 H5Dwrite(data, H5T_STD_U64LE, H5S_ALL, H5S_ALL, H5P_DEFAULT, ×tamp);
1565 long long datalength = period * fixedcount;
1566 data = H5Dcreate(file.trial,
"Trial Length (ns)", H5T_STD_U64LE,
1567 scalar_space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1568 H5Dwrite(data, H5T_STD_U64LE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &datalength);
1572 H5Sclose(scalar_space);
1573 H5PTclose(file.cdata);
1574 H5Gclose(file.sdata);
1575 H5Gclose(file.pdata);
1576 H5Gclose(file.adata);
1577 H5Gclose(file.sysdata);
1578 H5Gclose(file.chandata);
1579 H5Gclose(file.trial);
1581 H5Fflush(file.id, H5F_SCOPE_LOCAL);
1583 H5Fget_vfd_handle(file.id, H5P_DEFAULT, &file_handle);
1584 if (fsync(*
static_cast<int *
> (file_handle)))
1586 std::cout <<
"DataRecorder::Panel::stopRecording : fsync failed, running sync\n";
1596 DataRecorder::Plugin::Plugin(
void)
1599 QSettings userprefs;
1600 userprefs.setPath(QSettings::NativeFormat, QSettings::SystemScope,
"/usr/local/share/rtxi/");
1601 buffersize = (userprefs.value(
"/system/HDFbuffer", 10).toInt())*1048576;
1605 DataRecorder::Plugin::~Plugin(
void)
1607 while (panelList.size())
1608 delete panelList.front();
1615 panelList.push_back(panel);
1621 panelList.remove(panel);
1627 for (std::list<Panel *>::iterator j = panelList.begin(), end = panelList.end(); j != end; ++j)
1628 (*j)->deferred(s.
loadState(QString::number(i++).toStdString()));
1633 for (
size_t i = 0; i < static_cast<size_t> (s.
loadInteger(
"Num Panels")); ++i)
1636 panelList.push_back(panel);
1645 for (std::list<Panel *>::const_iterator i = panelList.begin(), end = panelList.end(); i != end; ++i)
1646 s.
saveState(QString::number(n++).toStdString(), (*i)->save());