XOP to include Measurement Computing functions

Howard Rodstein suggested he migh be able to help me with creating an XOP if I post a specific example of my need
Thanks Howard,

Measurement Computing has a library of functions in "C" which allow control of their modules.
The "universal library" is available for download, (http://www.mccdaq.com/software.aspx) along with an
extensive pdf file (www.mccdaq.com/PDFs/Manuals/universal-library-help.pdf)

I have the following function from their library to map into IGOR with an XOP
cbDOut(BoardNum, int PortNum, unsigned short DataValue) from cbw.h, using cbw32.lib and cbw32.dll

My progress continues but is halting as I attempt to understand what I need from the many many possiblities in the XOP manual.
There are many items unfinished in the attached source code, I include it to ease discussion.

Current path to XOP creation (based on reading pgs 1-154 in XOP manual)
1) Create new XOP called mcdoutw1 using "create new XOP project.pxp" (from IGOR xop6) based on example XOP1 from IGOR
2) use cmdTemplate = "mcdoutw1 number:n1, string:s1, number:n2" & run simpleloadwave.pxp (from IGOR xop6)
4) replace into mcdoutw1.cpp new code from above
5) modify mcdoutw1wincustom.rc line 1100 XOPC config line "XOPOp | utilOp | compilableOp,"
6) add to mcdoutw1.cpp the statements #include "..\cbw.h" & #include "mcdoutw1.h"
7) Delete extra unnecessary wave function dowave from mcdoutw1.cpp (left over from XOP1 example)
8) Modify executemyxop1 section to include defines and calls to new functions

Unsure:
A) What to do with myxop1(Wavehndl waveH) function in the code? Remark line above it suggests that it, rather than executemyxop1() is the code which executed when the xop is called????
B) From the cmdtemplate above you can see I have thought a string would be best for the "PortNum" data.
I do not understand this part of "C" apparently, how a type int can hold string values, but that is what apparently is required by the mcc function.
ref: www.mccdaq.com/PDFs/Manuals/universal-library-help.pdf page 360 cbDOut function, page 197 USB-DIO96H.

mcdoutw1.txt
Quote:
I have the following function from their library to map into IGOR with an XOP
cbDOut(BoardNum, int PortNum, unsigned short DataValue) from cbw.h, using cbw32.lib and cbw32.dll


Usually in cases where you are just wrapping existing function calls you are better off writing external functions than external operations. Operations are good when you have many options via flags or keywords. When you have a fixed number of parameters it is most straight-forward to wrap the C function in an Igor function by creating an external function. To do that you would start by cloning XFUNC1.

Writing functions is a bit simpler than writing operations so I think you will be best served by starting over using XFUNC1 as the starting point. Also read Chapter 6 of the XOP Toolkit manual which explains external functions. And read the source for XFUNC1 before you start to modify it. It's often best to read from the high-level functions (bottom of file) to the low-level functions (top of file).

Quote:
2) use cmdTemplate = "mcdoutw1 number:n1, string:s1, number:n2" & run simpleloadwave.pxp (from IGOR xop6)


I'm not sure why you are running SimpleLoadWave.pxp. When you cloned XOP1 to create MyXOP (or whatever you called it - you should use a meaningful name like MCDXOP) the cloning operation would have cloned "XOP1 Template.pxp" to "MCDXOP Template.pxp". You should modify and use that experiment to create your starter operation code.

Quote:
A) What to do with myxop1(Wavehndl waveH) function in the code? Remark line above it suggests that it, rather than executemyxop1() is the code which executed when the xop is called????


The MyXOP1 function was called XOP1 before the cloning. The XOP1 function is called as a subroutine from ExecuteXOP1 to do the actual work of the XOP1 operation, after ExecuteXOP1 has gathered the parameters. So you don't need MyXOP1 in your XOP but you may want to create an analogous subroutine to be called from your ExecuteMyXOP1. (The segregation into a higher level function, ExecuteXOP1, and a lower level function XOP1 makes it easier to read and understand both functions and avoids winding up with one massive and unreadable function that you would have otherwise.)

Quote:
B) From the cmdtemplate above you can see I have thought a string would be best for the "PortNum" data.

It seems that it would best be a numeric parameter which means a double in Igor. I don't see any reason to make it a string.

Quote:
I do not understand this part of "C" apparently, how a type int can hold string values, but that is what apparently is required by the mcc function.

I don't see anything about strings on page 36 or page 197.

A C int can hold numeric values from -2^31 to 2^31-1. It can not hold a string.





Thank You for your patience,

I expect that this may help others at some point in the future.

I choose the XOP name mcDoutw1 based on
mc = measurement computing
Dout = digital out function
w = my first initial
1 = pgm 1

I'd followed an "operation" XOP instead of "function" because the manual said this was the path if one did not wish return values.

Chapter 5 in the XOP guide emphasizes the use of a template called simpleloadwave.pxp to be used in processing cmdtemplate data into the execute, register, and parameter list sections in the xop code. Following that instruction I modified the function in proc0 to what I required
"cmdTemplate = "mcdoutw1 number:n1, string:s1, number:n2";" and then ran the function to produce a code output to the copy cache. If there are indeed other templates for creating these code areas automatically, which better suit simple XOPs, I am certainly willing to learn about them and use them.

I will review your suggestions and keep going. Working thru this posting has helped me better understand what is involved, even if it doesn't read that way.

---------
The need for strings in a parameter declared as int
I no doubt confused things referencing two manuals, the one I intend using here is from mccdaq.com.
The mcc guide http://www.mccdaq.com/PDFs/Manuals/universal-library-help.pdf page 360 addresses the syntax of the input parameters for the cbDOut() function.
cbDOut(int BoardNum, int PortNum, unsigned short DataValue)
"BoardNum can be 0 to 99
PortNum There are ... ports that are programmable as input or output...for these types, set PortNum to FIRSTPORTA."
DataValue - 0 to 255 for PORTA/PORTB, 0-15 for PORTCL/PORTCH"

The same source page 197, for the board USB-DIO96H lists:
"PortNum
FIRSTPORTA FIRSTPORTB FIRSTPORTCL FIRSTPORTCH
SECONDPORTA SECONDPORTB SECONDPORTCL SECONDPORTCL
THIRDPORTA THIRDPORTB THIRDPORTCL THIRDPORTCH
FOURTHPORTA FOURTHPORTB FOURTHPORTCL FOURTHPORTCH"

It may be that these are defined and assigned interger values in the cbw.h header. That aside, for the XOP parameter calls I believe it best to pass the string values above into the XOP if that's possible. Perhaps someone else has seen mccdaq calls like this and already worked it out.

As a check I've examined source code using this function to run under windows and the value there is coded:

int BoardNum = 0;
int PortNum = FIRSTPORTA;
DataValue= 15;
cbDOut(BoardNum, PortNum, DataValue);
Quote:
I choose the XOP name mcDoutw1 based on


Just to be clear, the XOP is the program that you are writing as a whole so the XOP name is the name for the program as a whole. A good name would be something like MCCDAQ. mcDoutw1 is the name of one external operation added by your XOP.

Quote:
I'd followed an "operation" XOP instead of "function" because the manual said this was the path if one did not wish return values.


That's one distinction between operations and functions. If want to do digital input or analog input or if you want to check the status of the board you are going to want a return value. Or you can use the return value to return an error code or 0 if success.

Quote:
Chapter 5 in the XOP guide emphasizes the use of a template called simpleloadwave.pxp to be used in processing cmdtemplate


I see. You can use SimpleLoadWave Template.pxp as an example or, if you started by cloning another XOP, you can use the cloned XOP's template experiment.

Quote:
If there are indeed other templates for creating these code areas automatically, which better suit simple XOPs, I am certainly willing to learn about them and use them.


The automatic generation of code is used with external operations, not with external functions. The syntax of external functions is pretty rigid and therefore fairly simple to write.

Quote:
There are ... ports that are programmable as input or output...for these types, set PortNum to FIRSTPORTA."


I suspect that FIRSTPORTA is a C macro created by #define. For example:
#define FIRSTPORTA 1
The C preprocessor substitutes 1 for FIRSTPORTA before the C compiler sees the source code. The C compiler sees 1, not FIRSTPORTA.

Quote:
That aside, for the XOP parameter calls I believe it best to pass the string values above into the XOP if that's possible.


My guess is that you are better off passing numbers to the XOP for numeric parameters. It is faster and relieves you of the responsibility of converting text keywords into numeric values.

If you want to use symbolic names in Igor you can use constants in your Igor procedure file, like this:
Constant FIRSTPORTA = 1
Function Test()
    Variable boardNum = 0
    Variable portNum = FIRSTPORTA
    Variable dataValue = 15
    mcdoutw1(boardNum, portNum, dataValue)
End


Thank You again for your help.

I'll be reviewing the function vs operation. I may want to return a code from the other operation as you mentioned.

I found the defines for "FIRSTPORT" etc. Including other libraries can indeed make code difficult to read.

--------------
/* Types of digital I/O Ports */
#define AUXPORT 1
#define FIRSTPORTA 10
#define FIRSTPORTB 11
#define FIRSTPORTCL 12
#define FIRSTPORTC 12
#define FIRSTPORTCH 13
#define SECONDPORTA 14
#define SECONDPORTB 15
#define SECONDPORTCL 16
#define SECONDPORTCH 17
#define THIRDPORTA 18
#define THIRDPORTB 19
#define THIRDPORTCL 20
#define THIRDPORTCH 21
#define FOURTHPORTA 22
#define FOURTHPORTB 23
#define FOURTHPORTCL 24
#define FOURTHPORTCH 25
#define FIFTHPORTA 26
#define FIFTHPORTB 27
#define FIFTHPORTCL 28
#define FIFTHPORTCH 29
#define SIXTHPORTA 30
#define SIXTHPORTB 31
#define SIXTHPORTCL 32
#define SIXTHPORTCH 33
#define SEVENTHPORTA 34
#define SEVENTHPORTB 35
#define SEVENTHPORTCL 36
#define SEVENTHPORTCH 37
#define EIGHTHPORTA 38
#define EIGHTHPORTB 39
#define EIGHTHPORTCL 40
#define EIGHTHPORTCH 41
There seems to be a conflict between the IGOR "C" headers and the measurement computing headers. In two instances they define using the same names, BADRANGE and IDLE. I'm guessing that these are fairly common and that this has come up before.

What is the best solution, given I have no access to the source code for either?

--------------------------------------------------------------
Summary of errors from compile logs
--------------------------------------------------------------
1>c:\users\public\documents\measurement computing\daq\c\cbw.h(55): warning C4005: 'BADRANGE' : macro redefinition
1> c:\program files (x86)\wavemetrics\xop toolkit 6\igorxops6\xopsupport\igorerrors.h(62) : see previous definition of 'BADRANGE' (TaskId:17)
1>c:\users\public\documents\measurement computing\daq\c\cbw.h(360): warning C4005: 'IDLE' : macro redefinition
1> c:\program files (x86)\wavemetrics\xop toolkit 6\igorxops6\xopsupport\xop.h(224) : see previous definition of 'IDLE' (TaskId:17)

Expansion: header code comparisons
1) #define BADRANGE
Measurement Computing cbw.h
* C/C++ Windows header for Measurement Computing Universal Library
/* System error code */
#define BADRANGE 30 /* Invalid range specified */

IGOR igorerrors.h
// From IgorErrors.h.
#define BADRANGE 18 // insufficient range

2) #define IDLE
Measurement Computing cbw.h
* C/C++ Windows header for Measurement Computing Universal Library
/* Status values */
#define IDLE 0

IGOR xop.h
// *** XOP Message Codes -- Codes passed from Igor to XOP ***
// Basic messages
#define IDLE 2 //

--------------------------------------------------------------
compile logs attached from visual studio express 2012
--------------------------------------------------------------
Quote:
There seems to be a conflict between the IGOR "C" headers and the measurement computing headers. In two instances they define using the same names, BADRANGE and IDLE. I'm guessing that these are fairly common and that this has come up before.


Don't #include XOP headers and MCC headers in the same file. Keep your MCC routines in one or more separate files that define routines you call from XOP code. #include the MCC headers from the MCC interface files only.

Alternatively you can do this:
#include "XOPStandardHeaders.h" #undef IDLE // IDLE is also #defined by MCC #undef BADRANGE // BADRANGE is also #defined by MCC #include "MCC.h"