NI Home
Cart Cart | Help
Company Events Academic NI Developer Zone Support Solutions Products & Services Contact NI MyNI

Currently Being Moderated

Run-time error checking: a personal solution

VERSION 4

Created on: Nov 10, 2006 5:51 PM by Roberto Bozzolo - Last Modified:  Jul 22, 2008 12:38 AM by WendyL


REQUIREMENTS:
Application Software: LabWindows/CVI Base Development System

During years I developed some useful tools for run-time error checking, trying to achieve a good level of detail useful both for the user and for the programmer. Here some informations on the solutions I developed.

 

This is not a complete a functional sample code: instead,  it is intended as some suggestions to be integrated and eventually adapted to your actual application and programming style and standards.During years I developed some useful tools for run-time error checking, trying to achieve a good level of detail useful both for the user and for the programmer. With the exception of some high-level operators that are aware of internal software specifications, user interest is to know that a serious error has raised and that the program is not working correctly, date and time the error raised can be useful too; advanced users and the programmer are more interested to function / source file / type of error and line where the error occurred. All of these informations could be either displayed on the screen or saved to disk (or both).My implementation is basically an extension / adaptation of the good errChk and nullChk macros already defined in the programmer's toolbox. My extended version of these macros simply adds the information about the line the error raised.// MACRO TO TRAP GENERAL ERRORS#ifndef errChkEx#define errChkEx(fCall) if (error = (fCall), error < 0) \ { line = __LINE__; \ goto Error; } \ else#endif#ifndef nullChkEx#define nullChkEx(fCall) if ((fCall) == 0) \ { error = UIEOutOfMemory; \ line = __LINE__; \ goto Error; } \ else#endifOne possible problem of these macros is that they always jump to a fixed label: this can be dangerous in case the error rises in a moment when a running test cannot simply be abandoned but needs to conclude in a safe way (some hardware needs to be halted, security provisions must be asserted and / or software functions are present that need to be correctly concluded).Since normally I manage these conditions in a loop in which a continuous check for errors and abnormal operating conditions is performed, I developed a second macro that in case of error simply issues a break statement to jump out of the loop, where all above mentioned ending operations are placed:#ifndef errOut#define errOut(fCall) if (error = (fCall), error < 0) \ { line = __LINE__; \ break; } \ else#endif(I normally don't have memory allocation or other functions that need a nullChk inside those loops)The next step was to extend this approach to other environments that may generate errors: data acquisition, serial communications and so on. The following macros are what I developed:// MACRO TO TRAP FILE I/O ERRORS (ANSI_C LIBRARY)#ifndef ioChk#define ioChk(fCall) if ((fCall), errno != ENOERR) \ { ioerr = errno; \ line = __LINE__; \ goto Error; } \ else#endif // MACRO TO TRAP FILE I/O ERRORS (FORMATTING & I/O LIBRARY)#ifndef fileChk#define fileChk(fCall) if (ioerr = (fCall), ioerr == -1) \ { ioerr = GetFmtIOError (); \ line = __LINE__; \ goto Error; } \ else#endif // MACRO TO TRAP RS232 ERRORS// Macro to end a test under execution#ifndef comChk#define comChk(fCall) if (comerr = (fCall), comerr < 0) \ { line = __LINE__; \ goto EndTest; } \ else#endif // Macro to exit from a loop#ifndef comOut#define comOut(fCall) if (comerr = (fCall), comerr < 0) \ { line = __LINE__; \ break; } \ else#endif // MACRO TO TRAP DAQ ERRORS// Macro to end a test under execution#ifndef daqChk#define daqChk(fCall) if (daqerr = (fCall), daqerr < 0) \ { line = __LINE__; \ goto EndTest; } \ else#endif// Macro to exit from a loop#ifndef daqOut#define daqOut(fCall) if (daqerr = (fCall), daqerr < 0) \ { line = __LINE__; \ break; } \ else#endifThe macros for exiting a loop were developed to handle an error in a loop during an daq asyncronous operation in progress: here an example with traditional daq.Note the use of macro __FUNCTION__ to know the function name I'm in: it is effective starting from CVI 6 on and I used it in a generic warning function ./****************************************************************************/ SomeTestFunction ( ){ double   xx = NULL; // ...  // Allocating dynamic memory nullChk (xx = calloc (....) ); // Preparing and running data acquisition daqChk (DAQ_Rate (dScanRate, 0, &iScanTB, &uScanInt)); daqChk (SCAN_Setup (1, nch, piChanVect, piGainVect)); daqChk (SCAN_Start (1, mm, nl, 3, 2, iScanTB, uScanInt)); tini = Timer (); do {  // Check data acquisition  // Here I must exit the loop in case of errors  daqOut (DAQ_Monitor (1, 2, 0, 10, iLet, &ulIdx, &stopped));   // Show measures and/or handle test execution, stop button and so on...  } while (Timer () - tini < timeOutLimit && !stopped); // Stopping daq operation in progress daqChk (DAQ_Monitor (1, 2, 0, 10, iLet, &ulIdx, &stopped)); DAQ_Clear (1); // ...Error:EndTest: if (xx) free (xx); if (daqerr < 0)  ErrorMessage (daqerr, ERRDAQ, line, __FUNCTION__, __FILE__) else if (error < 0)  ErrorMessage (error, ERRGEN, line, __FUNCTION__, __FILE__) else if (comerr < 0)  ErrorMessage (comerr, ERRCOM, line, __FUNCTION__, __FILE__) else if (ioerr < 0)  ErrorMessage (ioerr, ERRIO, line, __FUNCTION__, __FILE__) else if (stop)  MessagePopup ("Test stopped", "Test stopped by operator."); return 0;}ErrorMessage ( ) function is intended to give the operator an explicative message of what has happened: this version if the minimum implementation I developed, in some applications I added the abilty to save to a log file the error message. The function gives all details about the type of error and source file and location were it raised:// Error types handled in the program#define  ERRGEN   0#define  ERRCOM   1#define  ERRIO   2#define  ERRERRNO  3#define  ERRDAQ  4#define  ERRUNKNOWN  9 /***************************************************************************/void ErrorMessage (int err, int terr, int line, char *function, char file) // Prompt error message{ char msg512; SYSTEMTIME  dh;    // Requires <windows.h>  GetLocalTime(&dh); sprintf (msg, "%s %d, %d (%d:%d)\n", Month (dh.wMonth), dh.wDay, dh.wYear, dh.wHour, dh.wMinute); sprintf (msg, "%sError %d received in %s (file %s - line %d):\n", msg, err, function, file, line); strcpy (title, "Severe error"); switch (terr) {  case ERRGEN:     strcat (msg, GetGeneralErrorString (err)); break;  case ERRCOM:     strcat (msg, GetRS232ErrorString (err)); break;  case ERRIO:      strcat (msg, GetFmtIOErrorString (err)); break;  case ERRDAQ:     strcat (msg, GetNIDAQErrorString (err)); break;  case ERRERRNO:  case ERRUNKNOWN: strcat (msg, "No additional description available."); break; }  MessagePopup ("Severe error", msg); return;}As can be seen, I give no additional description for file I/O errors generated inside ANSI_C library: these are dependent from the OS and are not so familiar to me since I do prefere to use Formatting & I/O library functions.In multithreaded applications it is not a good practice to stop a thread waiting for operator response on a MessagePopup function: for these conditions I developed a separate version of ErrorMessage function that is issued with a PostDeferredCall in the main thread using a special structure to pass parameters to the function:typedef struct {        // ERROR HANDLING STRUCTURE int    err;            &nb sp;         // Error code int    terr;            &n bsp;        // Type of error int    line;            &n bsp;        // Line the error raised char   func64;                  // Function where the error raised char   fileMAX_PATHNAME_LEN;   // Source file where the error raised} errStruct;The warning message is issued as follows:/***************************************************************************/Some TestFunction ( ){   // ...Error:EndTest:  if (daqerr < 0 || error < 0 || comerr < 0 || ioerr < 0) {    xe = malloc (sizeof (errStruct));    if (xe == NULL) {      // Out of memory       // ?? Che fare?    }    else {      xe->line = line;      strcpy (xe->func, __FUNCTION__);      strcpy (xe->file, __FILE__);      if (daqerr < 0) {        xe->err = daqerr;        xe->terr = ERRDAQ;      }      else if (error < 0)        xe->err = error;        xe->terr = ERRGEN;      }      else if (comerr < 0) {        xe->err = comerr;        xe->terr = ERRCOM;      }      else if (ioerr < 0) {        xe->err = ioerr;        xe->terr = ERRIO;      }      PostDeferredCall (WarnMessage, (void )xe);    } else if (stop)  MessagePopup ("Test stopped", "Test stopped by operator."); return 0;}WarnMessage ( ) function in this case is structured this way:/**************************************************************************/void CVICALLBACK WarnMessage (void *callbackData)// Warning messages to the operator{  char        msg512;  SYSTEMTIME  dh;    // Requires <windows.h>  errStruct   *xe = NULL;  if (!callbackData) return;  // No error details  xe = (errStruct *)callbackData;  GetLocalTime(&dh);  sprintf (msg, "%s %d, %d (%d:%d)\n", Month (dh.wMonth), dh.wDay, dh.wYear, dh.wHour, dh.wMinute);  sprintf (msg, "%sError %d received in %s (file %s - line %d):\n", msg, xe->err, xe->function, xe->file, xe->line);  switch (xe->terr) {    case ERRGEN:     strcat (msg, GetGeneralErrorString (err)); break;    case ERRCOM:     strcat (msg, GetRS232ErrorString (err)); break;    case ERRIO:      strcat (msg, GetFmtIOErrorString (err)); break;    case ERRDAQ:     strcat (msg, GetNIDAQErrorString (err)); break;    case ERRERRNO:    case ERRUNKNOWN: strcat (msg, "No additional description available."); break;  }  MessagePopup ("Severe error", msg);  free (xe);  return;}Other requirements:

NI-DAQ is required to compile the source code as is: if you are not interested in using daq functions you can simply comment out the relevant code.

Change control:

Corrected code in SomeTestFunction () function according to Eren Balci notes.

 

Downloads:
Average User Rating
(0 ratings)




Salih Eren BALCI Salih Eren BALCI  says:

copy-paste typo

 

In the code block given below in the article, all inputs &quot;daqerr&quot; to ErrorMessage function as error code.

Shouldn't it differ for each type of error?

...

ErrorMessage (daqerr, ERRDAQ, line, __FUNCTION__, __FILE__)

else if (error &lt; 0)

ErrorMessage (daqerr, ERRGEN, line, __FUNCTION__, __FILE__)

else if (comerr &lt; 0)

ErrorMessage (daqerr, ERRCOM, line, __FUNCTION__, __FILE__)

else if (ioerr &lt; 0)

ErrorMessage (daqerr, ERRIO, line, __FUNCTION__, __FILE__)

...            &nbsp ;  

BetaCommunityContent BetaCommunityContent  says in response to Salih Eren BALCI:

copy-paste typo 

 

Thanks Eren for the advise: of course you are right and I have corrected the source code according to it.  :)            &nbsp ;  

ChristianL ChristianL  says:

Can you move the code out of the text of the main document and into a separate download file. The main document is very unreadable, and should not contain long sections of code. Any code in the main documented should be formatted to read easily.

More Like This

  • Retrieving data ...