04-28-2010 08:08 PM
I only declare OnDoneEvent for the moment. I do not think I need OntimerExpired?
OnTimerExpired is there to detect when something goes wrong and the task is not done within a reasonable period of time. If you don't use a separate timer for this, your done callback will never be called if the sample clock wire becomes disconnected or something, but your program will keep on handling UI events. Feel free to omit it.
Regarding your comment: Since this is a UI app,
you might want to replace the 2nd parameter (0) with DAQmx_Val_SynchronousEventCallbacks
to have DAQmx use Windows messages instead of a separate thread. */
You MIGHT means I HAVE TO? I do not understand this so I left it to (0).
No, "you might" means that you might have to, and you might not. I don't think you can update your UI from the DAQmx callback if the DAQmx callback isn't running in the UI thread, but I am not an MFC expert so I'm not 100% sure. It's probably safer to pass DAQmx_Val_SynchronousEventCallbacks in your case.
About the timer, for the moment I prefer not using the timer if it is not absolutely needed?
It is not absolutely needed. It is only there to detect when the task is not done within a reasonable amount of time.
This is my code on this part:
int32 CVICALLBACK CNIWrapper::OnDoneEvent(TaskHandle, int32 status, void *)
{
...
This will not work. The callback function must be a C style function, not a C++ member function. If you want to use a member function, you need to write a C-style function that calls your member function, and use the "void *callbackData" parameter to pass the "this" pointer to it:
extern "C" int32 CVICALLBACK DoneEventHandlerForNIWrapper(TaskHandle taskHandle, int32 status, void *callbackData)
{
CNIWrapper* wrapper = reinterpret_cast<CNIWrapper*>(callbackData);
return wrapper->OnDoneEvent(taskHandle, status);
}
void CNIWrapper::Start()
{
...
nErrorCode = DAQmxRegisterDoneEvent (m_hAnalogTask, 0, DoneEventHandlerForNIWrapper, this);
...
}
int32 CNIWrapper::OnDoneEvent(TaskHandle taskHandle, int32 status)
{
...
}
The C++ FAQ Lite has more information on this topic.
As for where to declare/define it: you can define the function body in the .cpp file. If you define it below the definition of CNIWrapper::Start, then you need to write a forward declaration (the function prototype without the actual function body) above CNIWrapper::Start.
When I compile my code I get this error.
I GET a compile error:
error C2664: 'DAQmxRegisterDoneEvent' : cannot convert parameter 3 from
'long (void)' to '
I think it has to do with the NIWRAPPER declaration below in my code which might be wrong:
int32 CVICALLBACK CNIWrapper::OnDoneEvent(TaskHandle, int32 status, void *)
Most of the NIWRAPPER functions start with void and this one I declare as int32 CVICALLBACK ... but I do not know if it right in order to try to integrate it into the NIWRAPPER.h or NIWRAPPER.cpp files.
Part of the error message didn't get copied into your RTF file, but I'm pretty sure this is because DAQmxRegisterDoneEvent expects a pointer to a function, not a pointer to a member function.
Brad
05-03-2010 11:10 AM
I made the changes you suggested:
I try to compile and I get first error in this part of the code on the return wrapper line:
// WEB EXAMPLE response from Brian NI support forum:
//int32 CVICALLBACK OnDoneEvent(TaskHandle, int32, void*);
//int32 CVICALLBACK OnDoneEvent(TaskHandle, int32);
extern "C" int32 CVICALLBACK DoneEventHandlerForNIWrapper(TaskHandle taskHandle, int32 status, void *callbackData)
{
CNIWrapper* wrapper = reinterpret_cast<CNIWrapper*>(callbackData);
return wrapper->OnDoneEvent(taskHandle, status);
}
I get my first error in this part of the code return wrapper line:
NIWrapper.cpp(28) : error C2660: 'OnDoneEvent' : function does not take 2 parameters
And in this other part of the code I get the 2nd error:
//int32 CVICALLBACK CNIWrapper::OnDoneEvent(TaskHandle, int32 status, void *)
int32 CNIWrapper::OnDoneEvent(TaskHandle taskHandle, int32 status)
{
//prepare data
int nSamples = m_nChannels*m_nSamplesPerChannel*m_nNoOfScans;
long nSamplesRead = 0;
int nErrorCode;
char szErrorMsg[1025];
m_pdReceiveBuffer = new double[nSamples];
if(m_pdReceiveBuffer==NULL){
AfxMessageBox(_T("Not enough memory to read data !"));
return;
}
//read data:
nErrorCode = DAQmxReadAnalogF64( m_hAnalogTask, //handle to the task
m_nSamplesPerChannel, //samples per channel
10.0, //timeout = 10s
DAQmx_Val_GroupByChannel, //Group by scan number (interleaved)
m_pdReceiveBuffer, //data buffer
nSamples, //buffer size in samples
&nSamplesRead, //samples Per Channnel Read
NULL); //reserved: must be NULL
if(nErrorCode!=0) //error encountered
{
DAQmxGetErrorString(nErrorCode, szErrorMsg, 1024);
AfxMessageBox(szErrorMsg);
return;
}
AddToSigmaArray();
//AverageSigmaArrays();
delete [] m_pdReceiveBuffer;
m_pdReceiveBuffer = NULL;
return 0;
}
The 2nd error I get after the int32 CNIWrapper:: line.
NIWrapper.cpp(462) : error C2511: 'OnDoneEvent' : overloaded member function 'long (unsigned long,long)' not found in 'CNIWrapper'
Bear in mind that I have also an NIWrapper.h file and this is what I have declared in this file:
#endif // _MSC_VER > 1000
#include "NIDAQmx.h"
class CNIWrapper
{
public:
double *m_pdReceiveBuffer2;
double * GetSigmaTop();
double * GetSigmaBottom();
void SetDigitalOutput(int nLine, int nState);
void AverageSigmaArrays();
void AddToSigmaArray();
void Read();
void InitNICard();
void Start();
void StopClear();
//int32 CVICALLBACK DoneEventHandlerForNIWrapper(); Took it out cos I do not know if it is right here
int32 OnDoneEvent();
CNIWrapper();
virtual ~CNIWrapper();
private:
//NI-DAQ text strings
const char* m_sDevice;
const char* m_sChannels;
//const char* m_sSampClk;
const char* m_sClk;
const char* m_sShift;
const char* m_sDigLines;
const char* m_sAnalogTask;
const char* m_sDigOutTask;
const char* m_sClkTask;
const char* m_sShiftTask;
const int m_nChannels;
const int m_nSamplesPerChannel;
//NI-DAQ manipulation variables
TaskHandle m_hAnalogTask;
TaskHandle m_hDigOutTask;
TaskHandle m_hClkTask;
TaskHandle m_hShiftTask;
BOOL m_bNICardInitialized;
int m_nNoOfScans;
unsigned char m_nDigLines[8];
//DAQ buffers
double m_dSigmaTop[128];
double m_dSigmaBottom[128];
double *m_pdReceiveBuffer;
};
#endif // !defined(AFX_NIWRAPPER_H__16E54CC1_ED6E_48A8_A00C_F7578D31B149__INCLUDED_)
05-05-2010 09:46 PM
The signature of OnDoneEvent() should be the same in the .h and the .cpp file. DoneEventHandlerForNIWrapper is not a member function so it should not be part of the class definition in the .h file, and it doesn't really need to be in the .h file at all.
Brad
05-11-2010 06:48 AM
The signature of OnDoneEvent() should be the same in the .h and the .cpp file. DoneEventHandlerForNIWrapper is not a member function so it should not be part of the class definition in the .h file, and it doesn't really need to be in the .h file at all.
Brad
Ok. It is now the same in both places. Compiles and runs OK.
Regarding the forward declaration (function prototype without body) before the START function:
Which one shall I use?
//int32 CVICALLBACK OnDoneEvent(TaskHandle, int32);
//int32 OnDoneEvent(TaskHandle, int32);
I compiled everything OK and now I get this error when trying to acquire data. It complains about a TASK name conflict. Probably has to do with m_hAnalogTask but i do not why this new code we implemented creates this conflict.
See the PRINTSCREEN ERROR I attach please and if possible let me know what you think is happening.
Thanks and regards.
Javier.
05-11-2010 01:09 PM
The free function (with 3 arguments) needs CVICALLBACK so that a pointer to this function can be passed to DAQmx. The member function (with 2 arguments) does not.
Regarding the errors, I would recommend setting a breakpoint on each call to DAQmxCreateTask(). Is it getting called more often than you expect? Is it getting passed the wrong task name?
Also, you can get more detailed information (like task names, channel names, attribute values, etc.) out of DAQmx errors by calling DAQmxGetExtendedErrorInfo() instead of DAQmxGetErrorString().
Brad
05-12-2010 10:53 AM
Remember that all functions are written inside the NIWrapper.cpp file which is not the DIALOG main file (displayDlg.cpp or diaplayDlg.h) (user interface) with buttons, etc.
In the end I just moved the implemented function (inside the NIWrapper.cpp file)
int32 CNIWrapper::OnDoneEvent(TaskHandle taskHandle, int32 status)
{
......
to before the START function:
and I still placed just in case, the forward declaration on top (first lines) of the file (inside the NIWrapper.cpp file):
int32 OnDoneEvent(TaskHandle, int32);
Hope this is correct.
Regarding the errors:
I replaced DAQmxGetErrorString() by DAQmxGetExtendedErrorInfo() and I get the following printscreen errors which I attach in this post. It complains about ANALOG IN or something like that.
I do not know how to set a breakpoint or debug properly. Sorry about that, I just do many activities together so I forget. I know it is a debugging option though.
In my NIWrapper.h file I have all these:
(like task names, channel names, attribute values, etc.)
class CNIWrapper
{
public:
double *m_pdReceiveBuffer2;
double * GetSigmaTop();
double * GetSigmaBottom();
void SetDigitalOutput(int nLine, int nState);
void AverageSigmaArrays();
void AddToSigmaArray();
// void Read();
void InitNICard();
void Start();
void StopClear();
//int32 CVICALLBACK DoneEventHandlerForNIWrapper();
int32 OnDoneEvent(TaskHandle taskHandle, int32 status);
CNIWrapper();
virtual ~CNIWrapper();
private:
//NI-DAQ text strings
const char* m_sDevice;
const char* m_sChannels;
//const char* m_sSampClk;
const char* m_sClk;
const char* m_sShift;
const char* m_sDigLines;
const char* m_sAnalogTask;
const char* m_sDigOutTask;
const char* m_sClkTask;
const char* m_sShiftTask;
const int m_nChannels;
const int m_nSamplesPerChannel;
//NI-DAQ manipulation variables
TaskHandle m_hAnalogTask;
TaskHandle m_hDigOutTask;
TaskHandle m_hClkTask;
TaskHandle m_hShiftTask;
BOOL m_bNICardInitialized;
int m_nNoOfScans;
unsigned char m_nDigLines[8];
//DAQ buffers
double m_dSigmaTop[128];
double m_dSigmaBottom[128];
double *m_pdReceiveBuffer;
};
05-12-2010 10:59 AM
I forgot to say that in the NIWrapper.cpp file I have the following where the Analog IN Task appears as in the error:
CNIWrapper::CNIWrapper()
: m_sDevice(_T("Dev1"))
, m_sChannels(_T("Dev1/ai0,Dev1/ai1"))
, m_sDigLines(_T("Dev1/line0:7"))
//, m_sSampClk(_T("ai/SampleClock"))
//, m_sClk(_T("Dev4/ctr0"))
//, m_sShift(_T("Dev4/ctr1"))
, m_sAnalogTask(_T("Analog IN Task"))
, m_sDigOutTask(_T("Digital OUT Task"))
//, m_sClkTask(_T("Clock Task"))
//, m_sShiftTask(_T("ShiftIN Task"))
, m_nChannels(2)
, m_nSamplesPerChannel(128)
, m_bNICardInitialized(FALSE)
, m_hAnalogTask(NULL)
, m_hDigOutTask(NULL)
{
int i;
for(i=0;i<128;i++){
m_dSigmaTop[i] = 0;
m_dSigmaBottom[i] = 0;
}
for(i=0;i<8;i++) m_nDigLines[i] = 0;//init the digital lines status table
m_pdReceiveBuffer = NULL;
//1 is initial value (in Delphi code it is entered by user)
m_nNoOfScans = 1;
}
05-12-2010 11:57 AM
I do not see any other place where I create this ANALOG task so therefore:
The error comes from this START function part in createtask:
void CNIWrapper::Start()
{
//NI-DAQ initialization
int nErrorCode;
char szErrorMsg[1025];
nErrorCode = DAQmxCreateTask(m_sAnalogTask, &m_hAnalogTask);
if(nErrorCode!=0) //error encountered
{
DAQmxGetExtendedErrorInfo(szErrorMsg, 1024);
AfxMessageBox(szErrorMsg);
}
...................
What shall I do?
I also saw that in this part of the code I create a digital task but it is not the ANALOG task. I tested it without this creation and it still gives me the same error:
void CNIWrapper::InitNICard()
{
//NI-DAQ initialization
int nErrorCode;
char szErrorMsg[1025];
nErrorCode = DAQmxCreateTask(m_sDigOutTask, &m_hDigOutTask);
if(nErrorCode!=0) //error encountered
{
DAQmxGetExtendedErrorInfo(szErrorMsg, 1024);
AfxMessageBox(szErrorMsg);
}
//Digital output channel (so far 1 line is used)
nErrorCode = DAQmxCreateDOChan( m_hDigOutTask, //task handle
m_sDigLines, //phisical digital lines names
NULL, //channel name NULL - automatic naming
DAQmx_Val_ChanPerLine);
if(nErrorCode!=0) //error encountered
{
DAQmxGetExtendedErrorInfo(szErrorMsg, 1024);
AfxMessageBox(szErrorMsg);
}
}
05-14-2010 12:31 PM
You need to figure out when your program is calling DAQmxCreateTask() and DAQmxClearTask(), because it is not clearing the task before creating a new one with the same name. One way is to learn how to use the debugger. Another way is to add calls to printf() or MessageBox() so that you can see when they get called.
Brad
05-17-2010 07:31 AM
I am clearing the task ok.
Can you check these lines of code? I think the problem is the relation between taskhandle and m_hAnalogTask. Somewhere in these lines of code the link is not established:
Lines of code in file NiWrapper.cpp:
I start declaring what you told me to add here at the top of the file (declarations of protoytpes, etc).
int32 OnDoneEvent(TaskHandle, int32);
extern "C" int32 CVICALLBACK DoneEventHandlerForNIWrapper(TaskHandle taskHandle, int32 status, void *callbackData)
{
CNIWrapper* wrapper = reinterpret_cast<CNIWrapper*>(callbackData);
return wrapper->OnDoneEvent(taskHandle, status);
}
I then create the task in the START function:
void CNIWrapper::Start()
{
//NI-DAQ initialization
int nErrorCode;
char szErrorMsg[1025];
nErrorCode = DAQmxCreateTask(m_sAnalogTask, &m_hAnalogTask);
if(nErrorCode!=0) //error encountered
{
DAQmxGetExtendedErrorInfo(szErrorMsg, 1024);
AfxMessageBox(szErrorMsg);
}
(still inside the START function) and then ....
//Analog voltage channel
nErrorCode = DAQmxCreateAIVoltageChan( m_hAnalogTask, ................. //task handle
(still inside the START function) and then ...
//Sample clock timing
nErrorCode = DAQmxCfgSampClkTiming (m_hAnalogTask, ..................... //task handle
(still inside the START function) and then ....
nErrorCode = DAQmxRegisterDoneEvent (m_hAnalogTask, //task handle
0, //If not, try DAQmx_Val_SynchronousEventCallbacks
DoneEventHandlerForNIWrapper, //
this); //
if(nErrorCode!=0) //error encountered
{
DAQmxGetExtendedErrorInfo(szErrorMsg, 1024);
AfxMessageBox(szErrorMsg);
}
(still inside the START function) and then ...
nErrorCode = DAQmxStartTask(m_hAnalogTask);
I then go on to:
int32 CNIWrapper::OnDoneEvent(TaskHandle taskHandle, int32 status)
{
........... then,
//read data:
nErrorCode = DAQmxReadAnalogF64( m_hAnalogTask, //handle to the task
I then go to:
void CNIWrapper::StopClear()
{
int nErrorCode;
char szErrorMsg[1025];
nErrorCode = DAQmxStopTask(m_hAnalogTask);
.......... then,
nErrorCode = DAQmxClearTask(m_hAnalogTask);
THEN, IN THE MAIN USER INTERFACE DIALOG I just have this everytime the timer executes:
//Start task
m_niCard.Start();
mySerialPort.Write("r");
double *dBufferTop1 = m_niCard.GetSigmaTop();
double *dBufferBot1 = m_niCard.GetSigmaBottom();
for(int z=0; z<128; z++)
{
dBufferTop[z] = dBufferTop1[z];
dBufferBot[z] = dBufferBot1[z];
}
//Stop and clear task
m_niCard.StopClear();
I THINK I AM MISSING the link between taskhandle and m_hAnalogTask.
Please reply as simple as possible (for dummies, -)) so that I can understand the link between taskhandle and m_hAnalogTask