我找到了一个关于如何使用某些DAQmx函数的NI示例。这是一个简单的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;
}
}
我找到了解决问题的方法。我在文件的顶部声明了一个数组变量。这样我的回调函数就能识别它。然后,我将数据从此数组复制到我的成员数组。 同样,我创建了一个计数器变量,并在每次回调运行时递增它。同时我在我的成员函数中循环检查这个变量,直到它达到理想值,然后发出一个信号。这种方法真的很糟糕,我希望找到一种更聪明的方式来编写它。
答案 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 :(得分:0)
正如我几小时前在评论中提到的,您有几种方法可以解决此问题:
1)使用比您当前使用的更高级别的库,您最好使用信号和插槽,然后在Qt应用程序中使用。
2)您可以尝试将您的类实例作为原始数据传递。这将确保回调将有一个对象可以使用。当然,这需要像往常一样从void *数据重新解释为所需的类型。
3)如果不是太复杂,你也可以自己在项目中实现必要的逻辑。这样,您甚至可以减少依赖性。
很难说哪种方法最适合您,因为它在很大程度上取决于用例和更多的上下文。
我可能会选择成员数据访问器和mutator方法,或者类中用于完成实际工作的专用函数,而自由回调函数只会将void *转换为您的类型,并调用该方法实例
这也将确保您可以稍后使用非c ++代码的回调,因为您只需要替换它的内部实现。这也有助于以后摆脱调用类方法的外部C回调。
我认为你应该查看下面的url,其中有一个相对详细的示例代码,其中还有一个针对此低级库的类似问题的回调。
答案 2 :(得分:0)
我们最近连接了一个NI USB模拟输入卡,用于六轴力传感器。联系回拨与Vinzenz&#39;相同。解。在我们的示例中,我们只需锁定并读取/写入缓冲矢量即可访问模拟电压值。我们的应用程序是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;
}
}
正如Vinzenz所指出的那样,将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);
希望这些部分例子很有用。