сделать CVICALLBACK функцией-членом в QT создателе

Я нашел пример NI о том, как использовать некоторые функции DAQmx. Это простой C-файл, содержащий некоторые из следующих элементов:

...
// This is a declaration/definition I think
int32 CVICALLBACK ChangeDetectionCallback(TaskHandle taskHandle, int32 signalID, void *callbackData);
...
// Later in the script there is actual function

int32 CVICALLBACK ChangeDetectionCallback(TaskHandle taskHandle, int32 signalID, void *callbackData)
{
...
return 0;
}

Когда я склонен использовать некоторые переменные или функции, которые определены в файле .h, функция ChangeDetectionCallback не распознает их. Я попытался определить эту функцию обратного вызова как функцию-член в файле .h, надеясь, что теперь все функции будут доступны. Вот мой контент .h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "NIDAQmx.h"
namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();

int32 CVICALLBACK ChangeDetectionCallback(TaskHandle taskHandle, int32 signalID, void *callbackData);

private:
Ui::MainWindow *ui;
void mainLoop();
};

#endif // MAINWINDOW_H

и вот мой контент .c:

#include "mainwindow.h"#include "ui_mainwindow.h"#include "NIDAQmx.h"
#include <stdio.h>
#include <string.h>
#include <time.h>

#define DAQmxErrChk(functionCall) if( DAQmxFailed(error=(functionCall)) ) goto Error; else

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mainLoop();
}

MainWindow::~MainWindow()
{
delete ui;
}

void MainWindow::mainLoop()
{
...
DAQmxErrChk (DAQmxRegisterSignalEvent(taskHandle,DAQmx_Val_ChangeDetectionEvent,0,ChangeDetectionCallback,NULL));
...
}int32 MainWindow::ChangeDetectionCallback(TaskHandle taskHandle, int32 signalID, void *callbackData)
{
...
return 0;
}

Итак, опять же, я попытался много неправильных способов определить мою функцию обратного вызова в заголовочном файле безуспешно. Пожалуйста, помогите мне разобраться.
И вот сообщение об ошибке, которое я не совсем понимаю:

D:\Projects\sapm3\mainwindow.cpp:37: error: cannot convert 'MainWindow::ChangeDetectionCallback' from type 'int32 (MainWindow::)(TaskHandle, int32, void*) {aka long int (MainWindow::)(void*, long int, void*)}' to type 'DAQmxSignalEventCallbackPtr {aka long int (__attribute__((__cdecl__)) *)(void*, long int, void*)}'
DAQmxErrChk (DAQmxRegisterSignalEvent(taskHandle,DAQmx_Val_ChangeDetectionEvent,0,ChangeDetectionCallback,NULL));

Вот оригинальный код. Он запускает функцию обратного вызова для получения образца измерения и выводит данные на консоль. Я хочу записать выбранные данные в мою переменную-член и подать сигнал, который определен в файле .h объекта.

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <NIDAQmx.h>

#define DAQmxErrChk(functionCall) if( DAQmxFailed(error=(functionCall)) ) goto Error; else

static TaskHandle   taskHandle;
static uInt32       numLines;
static uInt8        cachedData[200];

int32 CVICALLBACK ChangeDetectionCallback(TaskHandle taskHandle, int32 signalID, void *callbackData);
void Cleanup (void);

int main(void)
{
int32       error=0;
char        errBuff[2048]={'\0'};

/*********************************************/
// DAQmx Configure Code
/*********************************************/
DAQmxErrChk (DAQmxCreateTask("",&taskHandle));
DAQmxErrChk (DAQmxCreateDIChan(taskHandle,"Dev1/port0/line0:7","",DAQmx_Val_ChanPerLine));
DAQmxErrChk (DAQmxCfgChangeDetectionTiming(taskHandle,"Dev1/port0/line0:7","Dev1/port0/line0:7",DAQmx_Val_ContSamps,1));
DAQmxErrChk (DAQmxRegisterSignalEvent(taskHandle,DAQmx_Val_ChangeDetectionEvent,0,ChangeDetectionCallback,NULL));
DAQmxErrChk (DAQmxGetTaskNumChans(taskHandle,&numLines));

/*********************************************/
// DAQmx Start Code
/*********************************************/
DAQmxErrChk (DAQmxStartTask(taskHandle));

puts("Continuously reading. Press Enter key to interrupt\n");

puts("Timestamp                 Data read   Changed Lines");

getchar();

Error:
if( DAQmxFailed(error) )
{
DAQmxGetExtendedErrorInfo(errBuff,2048);
Cleanup();
printf("DAQmx Error: %s\n",errBuff);
}
printf("End of program, press Enter key to quit\n");
getchar();
return 0;
}

int32 CVICALLBACK ChangeDetectionCallback(TaskHandle taskHandle, int32 signalID, void *callbackData)
{
int32   error=0;
uInt8   data[200]={0};
int32   numRead;
uInt32  i=0;
char    buff[512], *buffPtr;
char    errBuff[2048]={'\0'};
char    *timeStr;
time_t  currTime;

if( taskHandle ) {
time (&currTime);
timeStr = ctime(&currTime);
timeStr[strlen(timeStr)-1]='\0';  // Remove trailing newline.

/*********************************************/
// DAQmx Read Code
/*********************************************/
DAQmxErrChk (DAQmxReadDigitalLines(taskHandle,1,10.0,DAQmx_Val_GroupByScanNumber,data,8,&numRead,NULL,NULL));

if( numRead ) {
buffPtr = buff;
strcpy(buff, timeStr);

strcat(buff,"  ");
buffPtr = buff + strlen(buff);
for(;i<numLines;++i) {
sprintf(buffPtr,"%d",data[i]);
buffPtr++;
}

strcat(buff,"    ");
buffPtr = buff + strlen(buff);
for(i=0;i<numLines;++i) {
sprintf(buffPtr,"%c",data[i]==cachedData[i]?'-':'X');
buffPtr++;
cachedData[i] = data[i];
}
puts(buff);
fflush(stdout);
}
}
return 0;

Error:
if( DAQmxFailed(error) )
{
DAQmxGetExtendedErrorInfo(errBuff,2048);
Cleanup();
printf("DAQmx Error: %s\n",errBuff);
}
return 0;
}

void Cleanup (void)
{
if( taskHandle!=0 )
{
/*********************************************/
// DAQmx Stop Code
/*********************************************/
DAQmxStopTask(taskHandle);
DAQmxClearTask(taskHandle);
taskHandle = 0;
}
}

Я нашел способ обойти мою проблему. Я объявляю переменную массива в верхней части файла. Таким образом, моя функция обратного вызова распознает это. Затем я копирую данные из этого массива в мой массив элементов.
Точно так же я создал переменную-счетчик и увеличиваю ее каждый раз, когда выполняется обратный вызов. В то же время я проверяю эту переменную в своей функции-члене, пока она не достигнет желаемого значения, а затем издаю сигнал. Такой подход действительно отстой, и я хочу найти более разумный способ написать это.

-1

Решение

Проблема в том, что вы пытаетесь передать указатель на функцию-член вместо указателя на функцию. Вы можете использовать косвенное обращение, чтобы заставить это работать.

Вне класса вы определите функцию:

int32 CVICALLBACK ChangeDetectionCallbackWrapper(TaskHandle taskHandle, int32 signalID, void *callbackData) {
MainWindow * this_ = reinterpret_cast<MainWindow*>(callbackData);
return this_->ChangeDetectionCallback(taskHandle, signalID);
}

Затем определите метод MainWindow для вызова следующим образом:

int32 ChangeDetectionCallback(TaskHandle taskHandle, int32 signalID);

А затем зарегистрируйте это так:

DAQmxRegisterSignalEvent(taskHandle,DAQmx_Val_ChangeDetectionEvent,0,ChangeDetectionCallbackWrapper,this));

Обратите внимание, что callbackData Параметр используется для передачи указателя на объект вокруг. Эти данные передаются при регистрации события, а не NULL,

Это типичный шаблон для библиотек C, и это типичный способ его подключения к C ++.

1

Другие решения

Как я отметил в комментариях пару часов назад, у вас есть несколько способов решить эту проблему:

1) Используйте библиотеку более высокого уровня, чем та, которую вы используете в настоящее время, где вы могли бы предпочтительно использовать сигналы и слоты, тогда как идеально в приложении Qt.

2) Вы можете попробовать передать экземпляр вашего класса как необработанные данные. Это гарантирует, что обратный вызов будет иметь объект для работы. Конечно, для этого потребуется, как обычно, переосмыслить данные void * до нужного типа.

  • Установить свойства мутатора и методы доступа для установки и получения членов
  • Объявите функцию обратного вызова как друга, но это обычно не рекомендуется.
  • Просто вызовите слот или простой метод из обратного вызова, чтобы сделать работу за вас.
  • Используйте функтор для выполнения работы
  • так далее…

3) Вы также можете реализовать необходимую логику внутри своего проекта, если он не слишком сложен. Таким образом, вы могли бы даже уменьшить зависимость.

Трудно сказать, какой подход подойдет вам лучше всего, так как он во многом зависит от вариантов использования и большего контекста, чем у нас здесь.

Я бы, вероятно, выбрал либо методы доступа к элементам данных и методы-мутаторы, либо выделенную функцию в вашем классе, которая выполняет реальную работу, а функция бесплатного обратного вызова просто привела бы void * к вашему типу и вызывала метод в этом экземпляре.

Это также гарантирует, что вы сможете использовать обратный вызов с кодом, не относящимся к c ++, так как вам потребуется заменить только его внутреннюю реализацию. Это также поможет позже избавиться от внешнего обратного вызова C, который вызывает метод класса.

Я думаю, что вы должны проверить следующий URL, где есть сравнительно подробный пример кода, где есть также обратный вызов для аналогичных проблем с этой библиотекой низкого уровня.

Код оболочки NI-DAQmx C ++

0

Недавно мы подключили плату аналогового ввода NI USB для использования с шестиосевым датчиком силы. Подключение обратных вызовов идентично решению Винценца. В нашем примере мы просто блокируем и читаем / записываем буферный вектор для доступа к аналоговым значениям напряжения. Наше приложение — wxWidgets, но библиотека окон не должна знать о обратных вызовах. Компилятор VC10, и программа построена на Win7, хотя она должна работать на Linux без изменений. Библиотека DAQmx продолжает перезванивать и заполнять массив необработанных данных выборочными напряжениями. Затем они усредняются и копируются в 6D вектор. Можно генерировать эти события, используя пользовательские события обратно в Qt или wx, если необходимо, например, с закомментированным To_main_msg_evt ниже. Одна вещь, которую я не понимаю, это то, что мы можем сойти с рук, не включая CVICALLBACK и это все еще работает. Это лучше оставить? Похоже, более общее решение без. Я также заметил, что обратные вызовы не начинают поступать в течение примерно трех секунд после инициализации карты.

//in ATI_force.hpp there is

int32 static Every_n_callback(  TaskHandle task_handle,
int32 every_n_samples_evt_type,
uInt32 n_samples,
void* obj_ref);

int32 Every_n_callback( TaskHandle task_handle,
int32 every_n_samples_evt_type,
uInt32 n_samples);//in ATI_force.cpp in the Init() function there is

for(int i = 0; i < 6; ++i)
{
channel_port = ports_names[i];
channel_name = channels_names[i];

if(still_ok)
{
still_ok = NI_ok(DAQmxCreateAIVoltageChan(  _task_handle,
channel_port.c_str(),
channel_name.c_str(),
DAQmx_Val_Cfg_Default,
-10.0, //min max volts params
10.0,
DAQmx_Val_Volts,
NULL));
}
}
if(still_ok)
{
//todo what is the 1000 param ->
//indicate continuous sampling at so many milliseconds (3rd param)
still_ok = NI_ok(DAQmxCfgSampClkTiming(_task_handle, "", _sample_every_ms, DAQmx_Val_Rising, DAQmx_Val_ContSamps, 1000));
}
if(still_ok)
{
//register the read callback Every_n_callbaint
int callback_every_n_samples(10); //<-effets speed of aquisition
still_ok = NI_ok(DAQmxRegisterEveryNSamplesEvent(_task_handle, DAQmx_Val_Acquired_Into_Buffer, callback_every_n_samples, 0, Every_n_callback, this));
}

//other useful functions

//used for the interface to the class
bool ATI_force::Read_all_channels(arma::vec6& vals_out)
{
bool success(false);
if(_is_initialized)
{
_chan_vals_mutex.lock();
vals_out = _chan_vals;
_chan_vals_mutex.unlock();
success = true;
}
return success;
}

//the callback and its static wrapper

int32 ATI_force::Every_n_callback(TaskHandle task_handle, int32 every_n_samples_evt_type, uInt32 n_samples, void* obj_ref)
{
ATI_force* obj_ptr(reinterpret_cast<ATI_force*>(obj_ref)); //obj_ref = "this"return obj_ptr->Every_n_callback(task_handle, every_n_samples_evt_type, n_samples);
}

int32 ATI_force::Every_n_callback(TaskHandle task_handle, int32 every_n_samples_evt_type, uInt32 n_samples)
{
int32 ret(-1);
bool still_ok(true);
//{
//  std::ostringstream oss;
//  oss << "In Every_n_callback: " << std::endl;
//  To_main_msg_evt ev(oss.str(), true);
//  wxPostEvent(_parent, ev);
//}
//lock the mutex on the data and write to it
_num_read = 0;
if(_is_initialized)
{
still_ok = NI_ok(DAQmxReadAnalogF64(_task_handle,
_num_samples,
_read_timeout_ms,
DAQmx_Val_GroupByChannel,
_data_buff.memptr(),  //writes over old vals
_data_buff.size(),
&_num_read,
NULL)); //this or NULL in last param?? todo
_chan_vals_buffer.zeros(); //zero out the values either way
if(still_ok)
{
//for all six channels
for(int j = 0; j < 6; ++j)
{
//average the samples
for(int i = j*_num_samples; i < (j + 1)*_num_samples; ++i)
{
_chan_vals_buffer.at(j) += _data_buff.at(i);
}
_chan_vals_buffer.at(j) /= static_cast<double>(_num_samples);
}

}
}
else
{
still_ok = false;
}
if(still_ok)
{

Condition_vals_out(_chan_vals_buffer);

_chan_vals_mutex.lock();
_chan_vals = _chan_vals_buffer; //this is the handoff to _chan_vals
_chan_vals_mutex.unlock();
}
if(still_ok)
{
ret = 0;
}
return ret;
}

//the usage in the main form is roughly

void M_Frame::On_ati_test_btn_click(wxCommandEvent& event)
{
if(!_ATI_force)
{
_ATI_force.reset(new ATI_force(this, 400.0, 50.0));
boost::posix_time::seconds wait_time(5);
boost::this_thread::sleep(wait_time);
}

double val_out(0.0);

arma::vec6 vals_out;
vals_out.zeros();

if(_ATI_force->Is_initialized())
{
//_ATI_force->Reset_bias();
std::cout << "_ATI_force Is Initialized." << std::endl;

int num_reads(5);
Stopwatch sw;
sw.Restart();
double total_time(0.0);
double avg_time(0.0);

_ATI_force->Bias_vals_out(vals_out);
for(int i = 1; i < num_reads; ++i)
{
if(_ATI_force->Read_all_channels(vals_out))
{
std::cout << "voltages =" << vals_out << std::endl;
}
else
{
std::cout << "Read failed." << std::endl;
}
}
total_time = static_cast<double>(sw.Get_elapsed_us());
avg_time = total_time/static_cast<double>(std::max(num_reads - 1, 1));
std::cout << "average read time = " << avg_time <<  "us" << std::endl;
}
}

Как указывает Винценц, это типично при подключении классов C ++ к обратным вызовам C. Другая библиотека, которая использует этот метод, является OpenCV. Здесь можно настроить обратные вызовы мыши, используя тот же шаблон без ведущих макросов и поставляемую оболочку C ++ cv::setMouseCallback для обратного вызова C cvSetMouseCallback,

//in .hpp

static void Mouse_handler(int event, int x, int y, int flags, void* param); //param = this
void Mouse_handler(int event, int x, int y, int flags);

//in Template_select()

//...
cv::namedWindow(_template_select, CV_WINDOW_AUTOSIZE);
cv::imshow(_template_select, _temp_select);
cv::waitKey(30);
cv::setMouseCallback(_template_select, Mouse_handler, this);

//...
cv::destroyWindow(_template_select);

Надеюсь, что эти частичные примеры полезны.

0
По вопросам рекламы [email protected]