We'll begin by getting a set of data from our Workshop server and displaying the output.
Windows: Start Visual Studio and create a new Project in the workspace. You can call it something like "WorkshopClientTest", if you'd like. In the project settings add Z:\KERNEL\Windows\lib32\include in the C/C++ Preprocessor category in the Additional include directories dialog. In the Link tab, General category, Object/Library modules, add wsock32.lib and Z:\KERNEL\Windows\lib32\tine.lib to the list
Linux: Create a C module with your favorite editor called WKClient.c. You can make a Makefile such as
WKClient: WKClient.o cc -o WKClient WKClient.c -lm -ltine chmod a+x WKClient WKClient.o: WKClient.c cc -c -I/usr/include/tine WKClient.c
to help you out with compiling and linking.
Add a C source file to the project and in the spirit of "A picture is worth a thousand words", add the following example code:
#include <stdio.h> #include <stdlib.h> #include "tine.h" int main(int argc, char *argv[]) { short cc; int i; DTYPE dout; float temperatures[100]; float sinevals[1024]; float amplitude; char errstr[256]; dout.dFormat = CF_FLOAT; dout.dArrayLength = sizeof(temperatures)/sizeof(float); dout.dTag[0] = 0; dout.data.vptr = temperatures; cc = ExecLinkEx("/WORKSHOP/WKSineGen/Device 0","Temperature",&dout,NULL,CA_READ,500); printf("Temperatures:\n"); if (cc == 0) { for (i=0; i<100; i++) printf("%f%s",temperatures[i],i<99 ? ", " : "\n"); printf("at %s\n",getDataTimeString(dout.dTimeStamp,TRUE)); } else { printf("error : %s\n",GetLastLinkError(cc,errstr)); } dout.dArrayLength = sizeof(sinevals)/sizeof(float); dout.data.vptr = sinevals; cc = ExecLinkEx("/WORKSHOP/WKSineGen/Device 0","Sine",&dout,NULL,CA_READ,500); printf("First 100 sine values:\n"); if (cc == 0) { for (i=0; i<100; i++) printf("%f%s",sinevals[i],i<99 ? ", " : "\n"); printf("at %s\n",getDataTimeString(dout.dTimeStamp,TRUE)); } else { printf("error : %s\n",GetLastLinkError(cc,errstr)); } dout.dArrayLength = 1; dout.data.vptr = &litude; cc = ExecLinkEx("/WORKSHOP/WKSineGen/Device 0","Amplitude",&dout,NULL,CA_READ,500); printf("Amplitude:\n"); if (cc == 0) { printf("%f\n",amplitude); printf("at %s\n",getDataTimeString(dout.dTimeStamp,TRUE)); } else { printf("error : %s\n",GetLastLinkError(cc,errstr)); } return cc; }
This looks like a lot of code, but it's really just copy, paste and edit pretty much the same thing for three different properties. There are essentially three different synchronous requests to get the data. The program displays the data at the console and then exits. Try compiling, linking and running this.
Let's see if we can add a monitor link for one of the properties, say "Temperature". We'll want to access our temperature array from a callback routine, so let's move the declarations for temperatures, sinevals, and amplitude outside of the main routine, so that they are globally available. Add a callback routine of prototype void (*)(int,int) called temperatureCb and fill it with the following code:
void temperatureCb(int id, int cc) { int i; char errstr[256]; if (cc == 0) { for (i=0; i<100; i++) printf("%f%s",temperatures[i],i<99 ? ", " : "\n"); printf("at %s\n",getDataTimeString(getCurrentDataTimeStamp(id),TRUE)); } else { printf("error : %s\n",GetLastLinkError(cc,errstr)); } }
Now we'll change the synchronous ExecLink() to get the temperature to an asynchonrous AttachLink(). Finally, before we return from main, well put in an infinite loop where we call SystemCycle(). When your done, your new code should look something like:
#include <stdio.h> #include <stdlib.h> #include "tine.h" float temperatures[100]; float sinevals[1024]; float amplitude; void temperatureCb(int id, int cc) { int i; char errstr[256]; if (cc == 0) { for (i=0; i<100; i++) printf("%f%s",temperatures[i],i<99 ? ", " : "\n"); printf("at %s\n",getDataTimeString(getCurrentDataTimeStamp(id),TRUE)); } else { printf("error : %s\n",GetLastLinkError(cc,errstr)); } } int main(int argc, char *argv[]) { short cc; int i; DTYPE dout; char errstr[256]; dout.dFormat = CF_FLOAT; dout.dArrayLength = sizeof(temperatures)/sizeof(float); dout.dTag[0] = 0; dout.data.vptr = temperatures; AttachLink("/WORKSHOP/WKSineGen/Device 0","Temperature",&dout,NULL,CA_READ,500,temperatureCb,CM_POLL); dout.dArrayLength = sizeof(sinevals)/sizeof(float); dout.data.vptr = sinevals; cc = ExecLinkEx("/WORKSHOP/WKSineGen/Device 0","Sine",&dout,NULL,CA_READ,500); printf("First 100 sine values:\n"); if (cc == 0) { for (i=0; i<100; i++) printf("%f%s",sinevals[i],i<99 ? ", " : "\n"); printf("at %s\n",getDataTimeString(dout.dTimeStamp,TRUE)); } else { printf("error : %s\n",GetLastLinkError(cc,errstr)); } dout.dArrayLength = 1; dout.data.vptr = &litude; cc = ExecLinkEx("/WORKSHOP/WKSineGen/Device 0","Amplitude",&dout,NULL,CA_READ,500); printf("Amplitude:\n"); if (cc == 0) { printf("%f\n",amplitude); printf("at %s\n",getDataTimeString(dout.dTimeStamp,TRUE)); } else { printf("error : %s\n",GetLastLinkError(cc,errstr)); } for (;;) SystemCycle(TRUE); return cc; }
If you run this now, you'll be updating every second with the new temperature values. Type 'quit' to exit the program (or if you're desparate, type ctrl-C).
Notice that we use the access mode of CM_POLL. This instructs the server to monitor the requested data at the given polling interval and to return the data at that interval. Another common access mode is CM_REFRESH. This instructs the server to monitor the data at the given polling interval, but to return the data only if they have changed since the last access (zero tolerance). If you use CM_REFRESH mode for our multi-channel temperature array, you will likely see and update every second anyway, because something is always changing. REFRESH mode is by itself very good for 'status' properties, i.e. properties which do not change their values very often. On the other hand, you may want to apply a tolerence regarding your callback notification. You can do this by calling the setNotificationTolerance() routine, passing say 20 as the percent tolerance you will allow. If you do this you should see fewer updates in your temperature readout.
Occasionally it is desirable to start a link, but nonetheless to know the initial results of the link before executing the next few lines of code. YOu can accomplish this by ORing CM_WAIT to the access mode (for instance CM_POLL|CM_WAIT or CM_REFRESH|CM_WAIT). You can try this out by printing something out immediately following the AttachLink(). Either you will see the results of the callback prior to your printout (with CM_WAIT) or you will see your printout (without CM_WAIT).
You can use the same routines within a VC++ GUI project. If you use MFC, you might need to link the tine32.lib library (which accesses a DLL version of the otherwise static tine.lib).
1.5.8