Main Page | Features | Central Services | csv-Files | Types | Transfer | Access | API-C | API-.NET | API-Java | Examples | Downloads
page generated on 18.05.2024 - 04:45
The buffered Server for C programs

C Programmers:

There is .. uh .. still a platform dependency. You're probably trying this out on a Windows machine or a Linux machine. Any other UNIX machine or a MAC should follow the Linux instructions below. The coding you will do is of course independent of platform, but setting up your project or make files will not be independent. ANd if your on a windows machine, it's probably windows 7/64-bit. If not you'll also have to make adjustments.

A "do-nothing" server

If you're using Windows, Start Visual Studio 2012 and create a new Console application Project (named "myserver" or whatever). Then add a new C file to the project called myserver.c (or a .cpp file called myserver.cpp

  • it doesn't really matter).

If you're using Linux, just make a working directory and create a new file with your favorite editor with the name of myserver.c (or your favorite name).

Back to windows and Visual Studio: Visual Studio is set up to cross compile and defaults to WIN32 code, which will also run on 64-bit windows. But we want to make 64-bit code. So do the following:

  • under PROJECT at the bottom of the menu you'll find 'myserver Properties'. Select this.
  • Select Configuration Properties and then the Configuration Manager
  • The Active solution platform is set to Win32 by default. Open up the combo box, select 'New' and then select x64 from among the choices (ARM is also offered).
  • Close the configuration manager and navigate to Configruation Properties -> C/C++ -> General.
  • On the upper right 'Configuration Combo', select 'All Configuration'.
  • Now click into the input area of 'Additional Include Directories'.
  • You should see a 'down arrow'. Click on this. Click on 'Edit'.
  • A new dialog will appear. Click on the input area and a 'browse' arrow will appear.
  • Browse your way to the TINE include directory (should be L: provided you have done a (subst L: C:\Users<username>\AppData\Local\tine).
  • on some versions of Visual Studio you might need to add the C/C++ Preprocessor directive TINE32_DLL.
  • Leave the C/C++ options and go to the Linker options.
  • Go to the 'General' category and click in the input area of 'Additional Library Directories'.
  • As in the case of 'Additional Include Directories', input (via browsing) the directory L:\LIB.
  • Now go to Linker -> Input.
  • In the 'Addtional Dependencies' input the following two libraries: tbufsrv64.lib and tine64.lib.
  • You can now hit the 'OK' button.

Now you're ready to write code.

To run your server you'll need to 'attach' it to your database files, so the minimum lines of code needed to make a running (buffered) tine server are the following (Unix):

#include <stdio.h>
#include "tbufsrv.h"
int main(int argc,char *argv[])
{
AttachServer("MYSINE",NULL,0);
runServer();
return 0;
}

In Windows you've probably just accepted whatever Visual Studio setup for you in which case, you're using pre-compiled headers and it started you off with a file called 'myserver.cpp'. You'll want to #include 'tbufsrv.h' and likewise attach to your server database. So add the the extra lines of code so that your 'myserver.cpp' module looks like:

#include "stdafx.h"
#include "tbufsrv.h"
int _tmain(int argc, _TCHAR* argv[])
{
int cc;
cc = AttachServer(NULL,"STAEQM",0);
if (cc != 0) printf("couldn't attach to database: %d\n",cc);
while (getchar() != 'q');
return 0;
}

Make sure that the equipment module name you pass is identical to the one that's in the database (TINE is case insensitive regarding all of the 'exposed' or 'exported' names but is case SENSITIVE regarding the local equipment module name as well as the FEC name.)

Note
The above applies equally well to Visual Studio 2005 as well as previous versions. However, you will have to do the following to build a Visual Studio 2005 solution: In the Property menu, Linker -> Input section, you should click on the empty field next to "Ignore Specific Library" and then input "LIBC.LIB". If you do not do this, Visual Studio 2005 will refuse to build the project.

In Linux you can either type:

cc -c -I/usr/include/tine myserver.c

cc -o myserver myserver.o -lm -ltine -ltbufsrv

chmod a+x myserver

at the command line or make a make file to do it for you:

myserver: myserver.o
cc -o myserver myserver.c -lm -ltine -ltbufsrv
chmod a+x myserver
myserver.o: myserver.c
cc -c -I/usr/include/tine myserver.c

You are now ready to run your server. In Windows, you can run it from inside Visual Studio or make an executable and run it. In Linux, you can run your server by typing

./myserver

You might have noticed something important right away. The device server "Export Name" or the local equipment module name will appear again in this code snippet.
This call to 'Attach' inside the above code snippet is a violation of the "A name belongs in one place only" ansatz of a configuration database. In the case of the standard tine server, this violation does not appear, since the standard server does not 'attach' to a database. However this is a small price to pay for an enormous simplification in the server API. And starting with the buffered server will help us understand more of the tine features early on, without having to plough through arcane configuration API calls. Note: if you use a fec.xml database, then you do NOT need to pass any database names in the AttachServer call (as the entire FEC configuration is contained in a single file).

Check the Log file (please !)

Before rushing to the Instant Client, have a look at the server's log file. This will be called 'fec.log' and should be located in c:\tine\log (if that's where FECLOG is pointing to!), or directly in the working area, if there's no FECLOG environment.

Near the end of the log file you should see a message saying that your equipment module was "registered with equipment name server". If you see this then everything went reasonably well so far.

15.12.13 13:38:15.549 CET[*unknown*] Attach Server to local equipment module SINEQM
15.12.13 13:38:15.549 CET[*unknown*] timer thread starting
15.12.13 13:38:15.551 CET[*unknown*] TineStopCycler has been called
15.12.13 13:38:15.552 CET[*unknown*] terminating local cycle
15.12.13 13:38:15.572 CET[*unknown*] timer thread ending
15.12.13 13:38:15.572 CET[*unknown*] flushing client and contract lists
15.12.13 13:38:15.573 CET[*unknown*] contract table flushed
15.12.13 13:38:15.575 CET[*unknown*] shutting down server ports
15.12.13 13:38:15.575 CET[*unknown*] SystemReset called (level 0)
15.12.13 13:38:15.575 CET[*unknown*] all equipment module memory returned to heap
15.12.13 13:38:15.594 CET[*unknown*] timer thread exited normally prior to wait
15.12.13 13:38:15.595 CET[*unknown*] closing timer thread handle
15.12.13 13:38:15.595 CET[*unknown*] closing system mutex set
15.12.13 13:38:15.596 CET[*unknown*] SystemReset called (level 0)
15.12.13 13:38:15.596 CET[*unknown*] all equipment module memory returned to heap
15.12.13 13:38:15.598 CET[*unknown*] Stock property mutex set initialized
15.12.13 13:38:15.598 CET[*unknown*] system mutexes initialized
15.12.13 13:38:15.599 CET[*unknown*] WIN32 host is little endian
15.12.13 13:38:15.601 CET[*unknown*] VERSION : 4.03.0012
15.12.13 13:38:15.601 CET[*unknown*] OS : WIN32
15.12.13 13:38:15.601 CET[*unknown*] Work Area: 65536
15.12.13 13:38:15.602 CET[*unknown*] Temp Size: 65536
15.12.13 13:38:15.604 CET[*unknown*] FEC: is running multi-threaded
15.12.13 13:38:15.604 CET[*unknown*] FEC HOME : [C:\Users\duval\AppData\Local\tine\servers\]
15.12.13 13:38:15.605 CET[*unknown*] Contract Table: reserved space for 1000 entries
15.12.13 13:38:15.605 CET[*unknown*] Client Table: reserved space for 100 entries
15.12.13 13:38:15.607 CET[*unknown*] alias table (FEC) : no such file
15.12.13 13:38:15.607 CET[*unknown*] exports file exports.csv (FEC): no such file
15.12.13 13:38:15.608 CET[*unknown*] scanning fecid.csv for FEC name
15.12.13 13:38:15.609 CET[*unknown*] C:\Users\duval\AppData\Local\tine\servers\fecid.csv : success
15.12.13 13:38:15.611 CET[*unknown*] unable to register as fec name is unknown!
15.12.13 13:38:15.611 CET[*unknown*] FEC: user access : NONE has DENIED access
15.12.13 13:38:15.611 CET[*unknown*] delaying system initialization
15.12.13 13:38:15.612 CET[*unknown*] RegisterExport : server (export) name -> (null), eqm (local) name -> SINEQM, num devices -> 1
15.12.13 13:38:15.614 CET[*unknown*] looking for exports for local name SINEQM
15.12.13 13:38:15.618 CET[*unknown*] get registered exports from file (24 entries)
15.12.13 13:38:15.619 CET[*unknown*] file SINEQM\devices.csv found
15.12.13 13:38:15.621 CET[*unknown*] device file for SINEQM : found
15.12.13 13:38:15.647 CET[*unknown*] exports file exports.csv (SINEQM): found
15.12.13 13:38:15.648 CET[*unknown*] scanning fecid.csv for FEC name
15.12.13 13:38:15.650 CET[*unknown*] export entry WinSineServer found!
15.12.13 13:38:15.653 CET[SINEGEN.7] accepting SINEGEN.7 as FEC name
15.12.13 13:38:15.654 CET[SINEGEN.7] SINEQM task registration: ---- ---- ----

Browsing your server

Now try to find your server in the Instant Client under the context name you assigned. You will more than likely need to click on the "Reload Names" menu item under options (if the context itself is new) or at a minimum open up and close again the context combo box (this will force a reload of the names under the given context).

You should see your device server and be able to browse the properties and devices you entered in the setup wizard.

You should be able to read a lot of '0' values (not very interesting).

A "do-something" server

So let's give the properties something to read back. Add a global data declaration for a float value called 'amplitude' and add a 'task()' routine that we will call in another part of our code:

float amplitude = 50.0;
void task(void)
{
int i;
float vals[1024], noise[1024];
for (i=0; i<1024; i++) noise[i] = ((float)rand()/10000.0);
for (i=0; i<1024; i++) vals[i] = 10.0 * noise[i];
pushBufferedData("Sine","Device0",(BYTE *)vals,1024,FALSE);
for (i=0; i<1024; i++) vals[i] = amplitude * sin(6.2832*(float)i/128.0) + noise[i];
pushBufferedData("Gaussian","Device0",(BYTE *)vals,1024,FALSE);
for (i=0; i<1024; i++) vals[i] = amplitude * exp(-0.0001*pow(((float)(i) - 200.0),2)) + noise[i];
pushBufferedData("Sine","Device1",(BYTE *)vals,1024,FALSE);
}

and inside your main routine, rearrange things so that this 'task()' routine is called:

int _tmain(int argc, _TCHAR* argv[])
{
int cc;
cc = AttachServer(NULL,"STAEQM",0);
if (cc != 0) printf("couldn't attach to database: %d\n",cc);
pushBufferedData("Amplitude","Device0",(BYTE *)&amplitude,1,FALSE);
while (TRUE)
{
task();
if (getchar() == 'q') break;
}
return 0;
}

So now task() is always called at startup and will be called again everytime you hit the keyboard, unless you hit 'q' in which case your server will stop.

You should now be able to read something for property Sine, Gaussian, and Amplitude at least for device = 'Device0'. We've also added some random values for property 'Sine' for 'Device1'. Try this out in the instant client.

You can make the other devices return something by following this example.

Try 'polling' from the Instant client. You won't see any data or data timestamp change unless you hit the keyboard on the server's console.

Let's have a sneek preview of what 'scheduling' is.

Poll the Sine property in the instant client, but see the update interval to 5 seconds.
As noted above, you won't see anything change unless you hit the famous 'any key' on the keyboard. Do this a couple of times and note when you actually see the data update on the instant client. You might notice that there is some latency, because the update interval of 5 seconds is stored at the server.
The server knows it should schedule a read request for at least one client every 5 seconds and it does so and then returns the data. But this is, at the moment, decoupled from when you hit the keyboard.

Now stop your server, find the spot it the task() routine where you call pushBufferedData for property 'Sine' (or whatever you're polling), and change the last parameter from FALSE to TRUE. This tells the system that the data for property 'Sine' has changed and to call the scheduler 'immediately' for any listeneing clients.

Now repeat the test, with the instant client polling at 5 seconds, and hit the keyboard. Notice any latency?

Making your server dynamic

Let's remove the 'getchar()' from our code and make the server a bit more dynamic. We'll simulate hardware i/o by calling our task() routine at regular intervals. Replace the getchar() with a sleep (i.e. millisleep() on windows) of e.g. 200 milliseconds (so we're updating at 5 Hz).

int _tmain(int argc, _TCHAR* argv[])
{
int cc;
cc = AttachServer(NULL,"STAEQM",0);
if (cc != 0) printf("couldn't attach to database: %d\n",cc);
pushBufferedData("Amplitude","Device0",(BYTE *)&amplitude,1,FALSE);
while (TRUE)
{
task();
millisleep(200);
}
return 0;
}

Adding device names

The clever device names proposed by the server wizard are found in the 'devices.csv' configuration file. You can change these be anything you would like, but the you should update your server code. To decouple the exported device name from your code, you should rather use '#0' instead of which ever name is configured for device number 0. That is, the TINE API calls will resolve a registered device name or the associated number, which passed as a string beginning with '#'. This would be the preferred way to code your server!

Try this out! Tweak your server code so that it uses "#0" instead of "Device0" and "#1" instead of "Device1". This shouldn't make any difference to any client. Now you can update your device.csv file to contain more meaningful names (assuming there are more meaningful names!) and not have to modify your server code.

Channel versus Spectrum Arrays

The instant client automatically chooses polyline draw mode for property 'Sine' and 'Guassian'. If you select property 'Amplitude', it will automatically choose historgram mode. Why? Property Amplitude is registered as a CHANNEL array, indicating that you can read an array of the current setting for ALL devices, where array element 0 refers to device 0, etc. Asking for a single value returns just just the setting for the given device. May properties (BPM positions, BLM losses, PSC settings, Vacuum Pressures) naturally lend themselves to multi-channel representation.

Try polling on a single device from the Amplitude property. In the Instant Client, lauch the debug window (you do this by setting the debug level to 1, but once the window is launched, you'll want to set the level back to 0). Have a look a the insant client's connection table. You'll see that the connection you want is actually bound to a connection which gets the entire array of values and maps the desired array element. This a valuable method of reducing load on a server, where the server ends up only ever doing ONE thing (and handling ONE interrupt), no matter what or how many clients are accessing portions of the multi-channel property.

Note
When a property returns a spectrum array (also known as a waveform or trace), it usually has x-axis units as well as y-axis units. (Voltage versus time for instance). The Wizard currently doesn't have room for x-axis units, but you can add it by hand by editing the export.csv file yourself. You need to find the description of the property in question (for instance "Sine" in our case). And instead of "[-100:100 V]Sine Curve", you would expand it to read for instance "[-100:100 V][-100:900 usec]Sine Curve". A client program reading this information would then know how to position the min and max settings for the horizontal axis as well as what the x-axis units are. HOWEVER: only make these kinds of changes when you're finished using the server wizard, otherwise you'll have to copy-and-paste them back in.

Now let's have a quick look at local histories. You can callup the server wizard to add specific history information, but for our purposes we can configure a property to keep a local history (with default settings) via a simple configuration tweak.

Find the export.csv file that the server wizard created and placed in the server's area under FEC_HOME (probably in L:\Servers\STAEQM) and open this file in your favorite editor (how about notepad?). If Excel is installed you might want to use excel as it load .csv files as a spreadsheet and you can easily locate columns and column data.

Find the 'ACCESS' column entry for property 'Sine' and property 'Amplitude'. For 'Sine', it should contain the string 'READ', and for 'Amplitude' it should contain the string 'READ|WRITE'. For both of these cases, append '|HIST' to entry, so that 'Sine' has ACCESS 'READ|HIST'and 'Amplitude' has 'READ|WRITE|HIST'.

When you restart your server, the values for 'Sine'and 'Amplitude' will be kept in a local history.

Note
This method of supplying a local history for a property uses only device = "#0". Thus is will work well for multi-channel arrays, which cover all registered devices, but only keep a history of a property for the initial device, and is not very convenient for keeping histories of the same waveform spectrum type property for multiple devices. It will serve our purposes of illustration here.
Otherwise you will need to supply specific targeted history information for those properties and devices desired.

Local histories

If you re-browse your server in the Instant Client, you should see a "history" check box appear, which will let you trivially check the history of the selected device. A better way to see what is going on, is to add by hand ".HIST" to the property name in the combo box (make sure the history check box is NOT checked !), and then hit the read button. Without any further input the local history server will ask for history data starting from "now" minus the depth in seconds given by the data size in the request. A property name ending in ".HIST" should automatically select a format type of "DBLDBL" which will return an array of data-timestamp pairs.

YOu can manually change this to the data type "DBLTIME" which signals the instant client to interpret the second double in a data pair as a time stamp.

Now we added some 'noise' to the property 'Sine' so the data are always changing, which means that the short term (ring buffer) local history is always being updated with new data and the long term (disk file) local history is likewise being updated if the data changes (for any point in the array) outside of the given tolerence (default settings set this to 10%).

The property 'Amplitude' doesn't change unless we set it to a new value. We shall do this shortly.

If you want to hava sneek preview of the local history viewer, you can call it up and under "Options" choose "Data Sources" and then choose the "LOcal History Chooser". You can then navigate via the Context combo box to the "WORKSHOP" context and find your server. It should display everything you are keeping a local history of. Choose one of your properties and devices and the click on "Add Selection".

Local Alarm Server

Now lets have a look at the local alarm system.
There is no 'quick way' to add alarm criteria to your buffered server configuration, other than via the server wizard. So start the wizard again, and select property "Amplitude" and check the "Alarm Watch" check box. You should change some of the settings in the alarm watch panel, namely the "Value too high" Threshold, you can set to 90, with a warning at 80 and the "Value too low" threshold to 10, with a warning at 20. Click the "Edit" button and then the "Done" button.

Note
And while we at it ... if you hit the 'Done' button and answer 'Yes' to the dialog about saving the files in the server configuration area, you will now also overwrite the exports.csv, with our "|HIST" additions. You can either put this information back in afterwards or before you hit the 'Done' button, take the opportunity to select properties 'Sine' and 'Amplitude' and check the 'Keep History' check box, followed by the 'Edit' button. This will not append the 'HIST' string to the property's 'ACCESS', but instead create a 'history.csv' file with the local history criteria.

You should now have generated an "almwatch.csv" file. Have a look at this file (if you like) and copy it into your FEC_HOME area and restart your server.

You can check the alarms on your server via the instant client, by including the "Stock Properties" in the property list. The relevant properties are "NALARMS" (number of alarms as a 5-parameter snapshot - when the first number > 0 then you have local alarms) and "ALARMS", which will return the current local alarm list (you might want to ask for fewer then the default 512 alarms).

Another simple way to view the alarms is to start the Remote Fec Control panel, find your server and click on the Alarms tab.

Note
You won't see any alarms until we set the Amplitude above 80 or below 20. We'll do that shortly.

Commands (changing settings)

Now let's see how we can set the amplitude.

You'll need to create a callback handler to intercept the incoming requests. Create a server callback routine with prototype int (*)(void). We'll make a general handler that reacts to a command, retrieves the input data, checks which property and device are being accessed and (after optionally decides to accept or reject the input) pushes the new data back into working buffers.

Consider for example:

void prpHndlr(int nid)
{
int cc = 0;
char prp[64],dev[64];
float fval;
getNotifiedPropertyAndDevice(prp,dev);
pullBufferedData(prp,dev,(BYTE *)&fval,1);
// decide here to accept or reject the input
amplitude = fval;
pushBufferedData(prp,dev,(BYTE *)&amplitude,1,TRUE);
SetBufferedPropertyCompletion(prp,cc);
}

Now register this callback handler in the main routine as follows: You'll want to call RegisterServerNotifier("Amplitude",prpHndlr) prior to launching your update loop. So you might end up with something like:

RegisterServerCallback("Amplitude",amplCb);

Try compiling and running your server now and setting the amplitude property. Inside the callback, you can also make use of the routines

hasInputChanged()

and

GetInputDeviceNumber()

to determined if and what input values have changed when the callback is called. This is useful if the same callback routine is used for different properties or if WRITE property needs to know which device was input.

Now use the Instant client (You can 'clone it very easily') to set the amplitude to a value other than '50'.

Note
unless you have already modified our simple code to keep an array of amplitudes (one for each 'device') then we're only setting one single global value of amplitude which used for everybody. But that's fine for now.

Now set the value to something larget then 80. You should see an alarm. In the instant client, check the 'Stock Properties' check box and choose the stock property 'ALARMS'. You should see someting like:

device-oriented versus property-oriented

Most classic device servers have many instances (devices) and each device supports the same set of properties. Many servers however are more "property-oriented" than not. Still others are more "device-oriented" than not.

Property-oriented would mean that each property potentially has a completely different set of devices. This is typical of middle-layer gateway servers, where there might be a property "VacPressure" and a property "Orbit-X". The getter pumps in the one case have nothing to do with the bpm monitors in the other.

The wizard is capable of setting up this kind of behavior in the configuration database. That is the purpose behind the Server or Property option button in the device panel. The default is "Server-oriented" device lists, where the devices given are valid for all properties on the server. If you select "Property", then a separate list is made specifically for the property in question. If you try this, you will generate an addition .csv file with the name <Property>-names.csv. In our case, "Temperature" might be such a property, so you would generate a Temperature-names.csv. (Of course if everything is called "Device 0", etc. you won't see the difference.

Device-oriented would mean that the device server supports all devices listed, but some devices might have a different property set than others. The wizard is not capable of setting up the Device-Oriented behavior, but you can add it yourself (fairly) easily.

You need to modify your devices.csv file. For instance it might look like:

DEVICE_NUMBER,DEVICE_NAME
0,Device 0
1,Device 1
2,Device 2
3,Device 3
4,Device 4
5,Device 5
...

You need to add by hand another column called PROPERTY_LIST. If the device in question deviates from the default property list (all registered properties), then you need to supply a file name (your choice) for the entry in this column. For instance, let's say that "Device 3" only supports property "Temperature". Then you would have:

DEVICE_NUMBER,DEVICE_NAME,PROPERTY_LIST
0,Device 0,
1,Device 1,
2,Device 2,
3,Device 3,Device3-properties.csv
4,Device 4,
5,Device 5,
...
Note
The above editing might be easier in Excel.

Then you need to create the file called Device3-properties.csv. This is a .csv file with one important column, namely "PROPERTY_NAME".

So you would have

PROPERTY_NAME
Temperature

And that would be that.

Note
You can't do both! Your server is going to be classic (one device list, one property list) or it is either device oriented or property oriented.

Once again, when you start editing the .csv files yourself, you'll probably want to stop using the wizard (or else get used to copy-and-paste).

Redirection

It's time to try Redirection. The wizard doesn't offer this, but you can do it again by hand.

Often it is the case that a server will offer a property which lives on another server, or a device which lives on another server.

Let's look at the device case first. Suppose you want to redirect any request involving Device 1 to your neigbor's server. Ask your neighbor what his server is called. Open up the <eqpmod>-devices.csv file and add a column called REDIRECTION and leave all the entries in this column blank except for the row corresponding to Device 1. In this row, put your neighbor's server. For example, if his server is called "MYNEIGHBORSERVER",you might have:

DEVICE_NUMBER,DEVICE_NAME,PROPERTY_LIST,REDIRECTION
0,Device 0,,
1,Device 1,,MYNEIGHBORSERVER
2,Device 2,,
3,Device 3,Device3-properties.csv,
4,Device 4,,
5,Device 5,,
...

Copy this new -devices.csv file to your FEC_HOME area and restart your server. When you (re)browse your server and access Device 2, your call will be redirected to MYNEIGHBORSERVER.

ONE IMPORTANT THING: If the "MYNEIGHBORSERVER" Server isn't running or doesn't have a device namded "Device 2" or a property equal to one you're trying to access then you will get an error code returned.

On to the Standard Server

We're finished with Buffered Servers, which maybe illustrate just how far you can get with databases alone. Sometimes you might already have a different database structure or more complicated needs, So you should now move on to the generated servers.

Server Groups

We should now leave the (legacy) server wizard behind as we're going to be adding columns to the
export.csv file (or tags to fec.xml).

In some cases it is desired to have a 'distributed' server, i.e. a server instance in the control system that effectively lives on multiple hosts. In TINE this is a server GROUP.

To see how this works, find the 'exports.csv' file again and get ready to edit it by hand. This time we want to add a column or two. Add the columns 'GROUP', 'GROUP_DEVICE_POSTFIX', and 'GROUP_INDEX'. And here you will see a minor disadvantage of .csv files, namely that there's no real hierarchy in a simple spreadsheet and information often gets repeated within a column.
This was already true of the columns 'EXPORT_NAME', 'LOCAL_NAME', and 'NUM_DEVICES' to the extent that exports.csv is mostly listing properties that belong to a single exported equipment modules (and can list others, but lets not worry about that now). Thus these columns contain the same information (actually, unless the local name changes, the NUM_DEVICES and EXPORT_NAME is fixed by the first entry, as will be the same for these new columns GROUP, GROUP_DEVICE_POSTFIX, and GROUP_INDEX.

Under 'GROUP' put the text 'Helgoland'. Under 'GROUP_DEVICE_POSTFIX' put your station number preceeded by a dot '.' (so '.1', '.2', etc.). Under Group_INDEX, put your station number.

Note
We need to worry about the GROUP_DEVICE_POSTFIX because there will very likely be device name collisions otherwise.

Only the first property is needed to record this information, but for appearances sake, you can make the entire column (for all properties) show the same information.

Note
in fec.xml there is a logical hierarchy where the same information fits within a single tag.

Now restart your server.

You should see now change in its behavior or in its registered and exported server name, properties, and devices, etc. But if you 'reload names' in the Instant Client you should now see another server called 'Helgoland'. If all stations participate, this new 'server' will offer 120 devices, all 'pointing' to their respective hosts.

Save and Restore

Some properties provice settings which change seldomly but are systematically important and it is important that any changes are not lost following the next server restart.

For instance, our default 'Amplitude' is '50 V'. But if someone (an operator?) changes this and expects it to keep its new value no matter whether the server is restarted then we need a mechanism to provide this. You can of course worry about this yourself and make your own initialization files, etc. However, TINE provides a simply way of establishing save-and-restore for any designated 'WRITE' property.

To try this out, edit the exports.csv file one more time. This time, find the property 'Amplitude' and locate the 'ACCESS' column again (maybe you have already re-applied the "|HIST" string here?). You now want to append the text string "|SAVERESTORE". This will mark this property as a 'save-and-restore' property.

You should now restart your server and set the 'Amplitude' to some value besides the default '50'.
Afterwards, restart your server again. Oops! This won't work as is. Why? Because (unless you do all the property registration per API) the buffered server will initialize the server in its entirety BEFORE you have a chance to set a 'notifier' for any of the properties, and the notifier MUST be in place during the server initialization or else the 'restoration' of property values can not work. You can't register the notifier first, because until you call 'AttachServer' it won't know that there is such a property!

This strategy will, however, work fine in the 'full blown' standard TINE server.

BUT, we can still do something here, that is only a little clumsy.

You will need to know which properties are marked as save and restore and address them in your code. If you modify your 'main' routine a little so that it explicitly calls RestorePropertyValues() then this can still work!

If you add these fourlines of code in the interior of your main routine

float vals[10];
RestorePropertyValues("STAEQM","Amplitude",vals,CF_FLOAT,10);
pushBufferedData("Amplitude","#0",(BYTE *)vals,10,FALSE);
amplitude = vals[0];

So that it now looks like:

int _tmain(int argc, _TCHAR* argv[])
{
int cc;
cc = AttachServer(NULL,"STAEQM",0);
if (cc != 0) printf("couldn't attach to database: %d\n",cc);
pushBufferedData("Amplitude","Device0",(BYTE *)&amplitude,1,FALSE);
RegisterServerNotifier("Amplitude",prpHndlr);
while (TRUE)
{
task();
millisleep(200);
}
return 0;
}

Then save and restore will work as expected.

Note
if your module is called e.g. myserver.c and not myserver.cpp then your using a straigh-up C compiler and it won't like to see variable such as vals[10] declared in the middle of the routine. You would then put parentheses '{}' around this section to achieve the same results.

This new code simple acquires the values to restore into an external array. Now you know that values which need to be pushed into the server. And don't forget to set the working variable 'amplitude' to the restored setting.

Security

All servers allow READ access by default. WRITE access will also be allowed until you 'do something about it'.
You can restrict the clients who are allowed to WRITE to the server according to user name and/or network address. This can be at the server level, the property level, or the device level.

You can also restrict the READ access by registering properties to have 'exclusive' READ, in which case the relevant properties follow the same security restrictions for READ calls as for WRITE calls.

It is suggested that you have a look at the Security section and practice adding both 'users.csv' and 'ipnets.csv' files to restrict access to your server.

RestorePropertyValues
TINE_EXPORT int RestorePropertyValues(const char *eqmName, const char *prpName, void *values, short format, int size)
Retrieves the value settings of the property name given from disk.
Definition: toolkit.c:2166

Impressum   |   Imprint   |   Datenschutzerklaerung   |   Data Privacy Policy   |   Declaration of Accessibility   |   Erklaerung zur Barrierefreiheit
Generated for TINE API by  doxygen 1.5.8