Main Page | Features | Central Services | csv-Files | Types | Transfer | Access | API-C | API-.NET | API-Java | Examples | Downloads
page generated on 04.05.2024 - 04:45
TINE Server Wizard

Introduction

TINE is object-based to the extent that device servers offer front-end information in the form of properties and devices. TINE properties can be read-only, write-only, or read-write and should be thought of as corresponding to methods (perhaps get/set methods) as in some cases (e.g. property “initialize”), a property could be simply a trigger. All properties are available via a variety of data access methods.

The current TINE server wizard addresses only the basic server functionality and not hardware IO. The goal is to present the server developer with a setup tool where he can input the functionality the server is supposed to have. The generated project will not have information as to the hardware IO and therefore contains numerous “TODO” statements at strategic locations in the code. Until the developer modifies the code to interface to the real hardware, the data generated for the properties will be simulated.

Note that this frequently follows what actually happens in real situations. Namely, an engineer thumbs through a catalog, chooses a hardware interface card which does what he wants and has the characteristics he needs, and implements it. As it comes with an ActiveX control, he easily builds a stand-alone data acquisition station, and then offers it to the beam diagnostics group, which wants it integrated into the control system as soon as possible. By making use of the TINE server wizard, this turns out to be an easy task .

A Practical Example

As an example consider the input parameters shown in the figure below:

The wizard selections above will generate either a C project and/or a Visual Basic project. Note: One could imagine generating, for instance, a LabView project as another alternative. Although the TINE ActiveX server control works fine with LabView, LabView uses a proprietary binary storage format which makes the rendering task difficult if not impossible.

Generated C Code

Suppose we select a "C" project from input selections. The TINE server wizard will generate the following equipment module:

/*
* bpmeqm.c REVISION HISTORY:
* Generated by SRVWIZARD on Fri Feb 27 09:05:50 2004
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include "tine.h"
#include "bpmeqm.h"
short g_online;
float g_orbit_yBuffer[PRP_ORBIT_Y_SIZE];
float g_orbit_xBuffer[PRP_ORBIT_X_SIZE];
void bpmeqm_bkg(void)
{
int i;
/* TODO: put your IO or other background activity here */
/* Clear alarms at start of IO task (see if they come back) */
ClearAlarm(BPMEQM_TAG,-1);
/* TODO: replace SimulateData() with your own data acquisition */
/* and manipulation routines */
for (i=0; i<PRP_ORBIT_Y_SIZE; i++)
{
g_orbit_yBuffer[i] = (float)SimulateData(-50,100);
}
/* TODO: replace SimulateData() with your own data acquisition */
/* and manipulation routines */
for (i=0; i<PRP_ORBIT_X_SIZE; i++)
{
g_orbit_xBuffer[i] = (float)SimulateData(-50,100);
}
/* make use of SetAlarm() as needed :
SetAlarm(BPMEQM_TAG,devNr,almCode,almFlag);
*/
}
/* Alternative to 'exports.csv': You can call 'RegisterBpmeqmProperties()' */
/* -> This might be preferable in cases where the server does not have a */
/* -> file system. You should also call RegisterServerName() in lieu of */
/* -> the 'fecid.csv' file. */
int RegisterBpmeqmProperties(void)
{
int cc = 0;
if ((cc=RegisterPropertyEx(BPMEQM_TAG,"ONLINE",PRP_ONLINE_SIZE,CF_SHORT,CA_READ|CA_WRITE,"[0:1 ]Read/Set on-line status",PRP_ONLINE)) < 0) goto err;
if ((cc=RegisterPropertyEx(BPMEQM_TAG,"ORBIT.Y",PRP_ORBIT_Y_SIZE,CF_FLOAT,CA_READ,"[-50:50 mm]Vertical Orbit",PRP_ORBIT_Y)) < 0) goto err;
if ((cc=RegisterPropertyEx(BPMEQM_TAG,"ORBIT.X",PRP_ORBIT_X_SIZE,CF_FLOAT,CA_READ,"[-50:50 mm]Horizontal Orbit",PRP_ORBIT_X)) < 0) goto err;
err:
if (cc < 0)
{
printf("Can't register property : %s\n>",erlst[-cc]);
}
return cc;
}
void bpmeqm_ini(void)
{
char devnam[16];
int i;
/* TODO: put your initialization here */
/* If you plan to use Property registration via API call rather than */
/* the exports.csv startup file, uncomment the following line: */
/* RegisterBpmeqm_iniProperties(); */
/* TODO: register any device names here */
/* below is only an example ... */
for (i=0; i<100; i++)
{
sprintf(devnam,"device_%d",i);
RegisterDeviceName(BPMEQM_TAG,devnam,i);
}
/* TODO: register any global receive routines as in this example */
/*
glbID = recvNetGlobal("HPMAGEN",&dout,rcvGlobal);
*/
/* TODO: start any links you might need as in this example */
/*
lnkID = AttachLinkEx("/HERA/BPM/WL197X","POSITIONS.X",&dout,&din,CA_READ,1000,showlink,CM_POLL,myid);
*/
}
void bpmeqm_exi(void)
{
/* TODO: put your shutdown routines here */
}
int bpmeqm(char *devName,char *devProperty,DTYPE *dout, DTYPE *din,short access)
{
int devnr,prpid,i,cc;
short l_online;
/* get device number from device name */
devnr = GetDeviceNumber(BPMEQM_TAG,devName);
/* you may want to make the following check: */
if (devnr < 0) return illegal_equipment_number;
/* TODO: If READ properties take input data, include code to examine the contents of din. */
/* If different actions need to be taken at the start or end of a link, examine the */
/* 'access' parameter against CA_FIRST or CA_LAST. */
/* If allow format overloading (you return different data according to the request */
/* format), then replace calls to putDataFromShort() etc with the desired code. */
prpid = GetPropertyId(BPMEQM_TAG,devProperty);
switch (prpid)
{
case PRP_ONLINE:
if (access&CA_WRITE)
{
if (din->dArrayLength > 0)
{
if (din->dArrayLength > PRP_ONLINE_SIZE) return dimension_error;
if ((cc=getValuesAsShort(din,&l_online,PRP_ONLINE_SIZE)) != 0) return cc;
if (l_online > PRP_ONLINE_UPR_LIMIT) return out_of_range;
if (l_online < PRP_ONLINE_LWR_LIMIT) return out_of_range;
g_online = l_online;
}
}
if (dout->dArrayLength > 0)
{
if (dout->dArrayLength > PRP_ONLINE_SIZE) return dimension_error;
if ((cc=putValuesFromShort(dout,&g_online,PRP_ONLINE_SIZE)) != 0) return cc;
}
return 0;
case PRP_ORBIT_Y:
if (access&CA_WRITE) return illegal_read_write;
if (dout->dArrayLength > 0)
{
if (dout->dArrayLength > PRP_ORBIT_Y_SIZE) return dimension_error;
if ((cc=putValuesFromFloatEx(dout,g_orbit_yBuffer,PRP_ORBIT_Y_SIZE,devnr)) != 0) return cc;
}
return 0;
case PRP_ORBIT_X:
if (access&CA_WRITE) return illegal_read_write;
if (dout->dArrayLength > 0)
{
if (dout->dArrayLength > PRP_ORBIT_X_SIZE) return dimension_error;
if ((cc=putValuesFromFloatEx(dout,g_orbit_xBuffer,PRP_ORBIT_X_SIZE,devnr)) != 0) return cc;
}
return 0;
default:
}
}

This module contains the "meat" of the server's behavior and provides an equipment function handler for processes requests for Property information, a background task which will simulate data but can otherwise be used to supply the data IO, initialization and exit routines, all with numerous 'TODO' comments and 'tips' and 'suggestions'.

In addition, the server's 'main' routine is generated, where various server settings can be tweaked:

/*
* devsrv REVISION HISTORY:
* Generated by SRVWIZARD on Fri Feb 27 09:05:49 2004
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include "tine.h"
#include "bpmeqm.h"
int SimulateData(int start,int range)
{
/* This routine is useful for test servers */
return start + rand()%(range);
}
char *SimulateStringData(int length)
{
static char lclStringBuffer[512];
time_t t = time(NULL);
static int counter = 1;
/* This routine is useful for test servers */
sprintf(lclStringBuffer,"%d : Simulated String generated %s",counter++,ctime(&t));
if (length < 0) length = 0;
if (length < 512) lclStringBuffer[length] = 0;
return lclStringBuffer;
}
void PreSystemInit(void)
{
/* Some System Parameters and their default values */
/* are shown below. You may change them as necessary */
MinPollingRate = 1000;
MaxPollingRate = 30000;
MaxNumConnections = 100;
FeclogDepth = 100;
StartupDebug = TRUE;
RequireAcknowledgments = TRUE;
MaxRPCTransportSize = 65535;
/* If you plan to use FEC Name registration via API call rather than */
/* the fecid.csv startup file, then uncomment the following line */
/*
RegisterFecNameEx("MSTXPDUVAL02.0","duval's Test FEC","WIN32C","duval's office","none","duval",0,"TEST");
*/
}
void PostSystemInit(void)
{
/* register equipment modules here: */
RegisterEquipmentModule(NULL,BPMEQM_TAG,0,bpmeqm,bpmeqm_ini,bpmeqm_bkg,BPMEQM_RATE,bpmeqm_exi);
/* Alternative: you can forgo the 'exports.csv' file by registering all */
/* properties via API call and supplying the exported device server name as */
/*
RegisterEquipmentModule("TTFBPM",BPMEQM_TAG,0,bpmeqm,bpmeqm_ini,bpmeqm_bkg,BPMEQM_RATE,bpmeqm_exi);
*/
/* TODO: add any remaining general initialization code here */
}
int main(int argc, char *argv[])
{
int cc;
/* TODO: modify command line input to suit your own needs. */
/* Here: the debug level is set according to initial input */
switch (argc)
{
case 2:
tineDebug = atoi(argv[1]);
break;
default:
tineDebug = 0;
}
/* To deviate from default settings, initialize */
/* any system variables in PreSystemInit() */
PreSystemInit();
/* initialize RPC server: */
if ((cc=SystemInit(TRUE)) != 0)
{
printf(erlst[cc]);
exit(1);
}
/* Make all other registrations in PostSystemInit() */
PostSystemInit();
for(;;)
{
/* TODO: add any additional non-TINE processing here */
/* being careful not to block operations */
SystemCycle(TRUE);
}
return 0;
}

Using the generated 'fec.mak' file you can compile and link and build a server executable, which will happily deliver simulated data. Using this code as a starting point, the developer can quickly see what he needs to do to interface his hardware data.

Generated VB Code

Suppose we select a "VB" project from input selections. The TINE server wizard will generate the following equipment module:

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' BpmeqmModule.bas REVISION HISTORY:
' Generated by SRVWIZARD on Fri Feb 27 09:05:51 2004
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Global variables and constants
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Const BpmeqmLocalName$ = "BPMEQM"
Private Const BpmeqmNumDevices% = 100
Global Const PRP_ONLINE_SIZE = 1
Global Const PRP_ONLINE_UPRLIMIT = 1
Global Const PRP_ONLINE_LWRLIMIT = 0
Global Const PRP_ORBIT_Y_SIZE = 100
Global Const PRP_ORBIT_Y_UPRLIMIT = 50
Global Const PRP_ORBIT_Y_LWRLIMIT = -50
Global Const PRP_ORBIT_X_SIZE = 100
Global Const PRP_ORBIT_X_UPRLIMIT = 50
Global Const PRP_ORBIT_X_LWRLIMIT = -50
Global prpOnline As Integer
Global prpOrbit_yBuffer(PRP_ORBIT_Y_SIZE - 1) As Single
Global prpOrbit_xBuffer(PRP_ORBIT_X_SIZE - 1) As Single
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' BpmeqmSimulateData is a useful routine for test servers
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function BpmeqmSimulateData(offset As Double, range As Double) As Double
BpmeqmSimulateData = offset + range * Rnd
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' include BpmeqmBackgroundFunction in a Timer to perform background IO, etc.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function BpmeqmBackgroundFunction(srvControl As Srv) As Integer
Dim i As Integer
' TODO: put your IO or other background activity here
' Clear alarms at start of IO task (see if they come back)
srvControl.EqpClearAlarm -1
' TODO: replace BpmeqmSimulateData() with your own data acquisition
' and manipulation routines
For i = 0 To PRP_ORBIT_Y_SIZE - 1
prpOrbit_yBuffer(i) = BpmeqmSimulateData(PRP_ORBIT_Y_LWRLIMIT, PRP_ORBIT_Y_UPRLIMIT - PRP_ORBIT_Y_LWRLIMIT)
Next
For i = 0 To PRP_ORBIT_X_SIZE - 1
prpOrbit_xBuffer(i) = BpmeqmSimulateData(PRP_ORBIT_X_LWRLIMIT, PRP_ORBIT_X_UPRLIMIT - PRP_ORBIT_X_LWRLIMIT)
Next
' make use of SetAlarm() as needed :
' srvControl.EqpSetAlarm devNr, almCode, CADDR(almData(0)), 0
' set error codes as needed:
BpmeqmBackgroundFunction = 0
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' include BpmeqmInitFunction in Form_load (for instance) to initialize the server
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function BpmeqmInitFunction(srvControl As Srv, expName As String) As Integer
Dim rc As Integer, id As Integer
' FECNAME: either read from file 'fecid.csv' or user API :
'rc = RegisterFecName(fecname$, desc$, loc$, hdw$, resp$, portoffset)
' DEVICE SERVER: device server parameters
srvControl.EqpNumberModules = BpmeqmNumDevices%
srvControl.EqpName = BpmeqmLocalName$
srvControl.ExportName = expName
'now enable the server
srvControl.Enabled = True
rc = srvControl.EqpStatus
If rc Then GoTo ExitBpmeqmInitFunction
'Property Registration:
id = srvControl.EqpRegisterPropertyEx("ONLINE", PRP_ONLINE_SIZE, CF_SHORT, "", PRP_ONLINE_SIZE, CF_SHORT, "", CA_READ + CA_WRITE, "[0:1 ]Read/Set on-line status")
If id < 0 Then GoTo ExitBpmeqmInitFunction
id = srvControl.EqpRegisterPropertyEx("ORBIT.Y", 0, CF_NULL, "", PRP_ORBIT_Y_SIZE, CF_FLOAT, "", CA_READ, "[-50:50 mm]Vertical Orbit")
If id < 0 Then GoTo ExitBpmeqmInitFunction
id = srvControl.EqpRegisterPropertyEx("ORBIT.X", 0, CF_NULL, "", PRP_ORBIT_X_SIZE, CF_FLOAT, "", CA_READ, "[-50:50 mm]Horizontal Orbit")
If id < 0 Then GoTo ExitBpmeqmInitFunction
'Device Name Registration:
' TODO: fill in the devices name which make sense for your server
For i = 0 To BpmeqmNumDevices% - 1
rc = srvControl.EqpRegisterModule("DEVICE" + Trim$(str$(i)), i)
If rc Then GoTo ExitBpmeqmInitFunction
Next
' Everything should be okay, check for deeper problems:
BpmeqmInitFunction = SRVSTATUS
'
ExitBpmeqmInitFunction:
If rc Then
BpmeqmInitFunction = rc
ElseIf id < 0 Then
BpmeqmInitFunction = -id
End If
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' include BpmeqmEqpFcn in the EqpFcn event of the Srv control
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub BpmeqmEqpFcn(srvControl As Srv, ByVal devName As String, ByVal devProperty As String, ByVal outArrayLen As Long, ByVal inArrayLen As Long, ByVal devAccess As Integer)
Dim i As Integer
Dim devNr As Integer
Dim lclOnline As Integer
' If your properties make use of the device number associated with the device name:
devNr = srvControl.EqpGetModuleNumber(devName)
If devNr < 0 Then
srvControl.EqpSetCompletion illegal_equipment_number, ""
Exit Sub
End If
' check which property was asked for:
Select Case devProperty
Case "ONLINE"
If (devAccess And CA_WRITE) Then
If inArrayLen = 0 Then
srvControl.EqpSetCompletion illegal_read_write, ""
Exit Sub
End If
' Get the incoming data:
srvControl.EqpRecvData lclOnline
' Do range checking:
If lclOnline > PRP_ONLINE_UPRLIMIT Then
srvControl.EqpSetCompletion out_of_range, ""
Exit Sub
End If
If lclOnline < PRP_ONLINE_LWRLIMIT Then
srvControl.EqpSetCompletion out_of_range, ""
Exit Sub
End If
' Copy incoming data into global data buffer
prpOnline = lclOnline
End If
If outArrayLen > 0 Then
srvControl.EqpSendData prpOnline
End If
Case "ORBIT.Y"
If (devAccess And CA_WRITE) Then
srvControl.EqpSetCompletion illegal_read_write, ""
Exit Sub
End If
If (outArrayLen > 0) And (outArrayLen <= (PRP_ORBIT_Y_SIZE - devNr)) Then
srvControl.EqpSendData prpOrbit_yBuffer(devNr)
Else
srvControl.EqpSetCompletion dimension_error, ""
Exit Sub
End If
Case "ORBIT.X"
If (devAccess And CA_WRITE) Then
srvControl.EqpSetCompletion illegal_read_write, ""
Exit Sub
End If
If (outArrayLen > 0) And (outArrayLen <= (PRP_ORBIT_X_SIZE - devNr)) Then
srvControl.EqpSendData prpOrbit_xBuffer(devNr)
Else
srvControl.EqpSetCompletion dimension_error, ""
Exit Sub
End If
Case Else
srvControl.EqpSetCompletion illegal_property, ""
Exit Sub
End Select
' If it's made it this far, it's a success:
srvControl.EqpSetCompletion 0, ""
End Sub

This module contains the "meat" of the server's behavior and provides an equipment function handler for processes requests for Property information, a background task which will simulate data but can otherwise be used to supply the data IO, initialization and exit routines, all with numerous 'TODO' comments and 'tips' and 'suggestions'.

In addition, the server's main form is generated, where various server settings can be tweaked:

Private Sub Form_Load()
Dim cc As Integer
cc = BpmeqmInitFunction(SrvBpmeqm, "TTFBPM")
If cc Then goto SrvInitError
SrvInitError:
If cc Then
StatusLabel.BackColor = &HFF ' RED
StatusLabel.Caption = RPCERROR(cc)
Else
StatusLabel.BackColor = &HFF00& ' GREEN
StatusLabel.Caption = "Server running"
End If
End Sub
Private Sub SrvBpmeqm_EqpFcn(ByVal devName As String, ByVal devProperty As String, ByVal outArrayLen As Long, ByVal inArrayLen As Long, ByVal devAccess As Integer)
BpmeqmEqpFcn SrvBpmeqm, devName, devProperty, outArrayLen, inArrayLen, devAccess
End Sub
Private Sub IOTimer_Timer()
BpmeqmBackgroundFunction SrvBpmeqm
End Sub

Using the generated '.vbp' project file you can compile and link and build a server executable, which will happily deliver simulated data. Using this code as a starting point, the developer can quickly see what he needs to do to interface his hardware data.

Remarks

The TINE server wizard is currently a “one-pass” wizard. This means that there are no “tags” within the generated code which separate the “hands-off” regions from the code sections the developer is allowed to change.

The code generation process consists of supplying the server behavior information through an input panel. This input panel exists as either a VB program or a TCL script. The specifications then generate an intermediate repository. The server wizard uses the TINE “exports.csv” file as a repository, since it is itself useful following the code generation. The repository is then rendered into the desired language.

The generated 'exports.csv' file is shown below:

EXPORT_NAME,LOCAL_NAME,PROPERTY,PROPERTY_SIZE,FORMAT,PROPERTY_INSIZE,INFORMAT,PROPERTY_ID,ACCESS,NUM_MODULES,DESCRIPTION,NUM_STEPS
TTFBPM,BPMEQM,ORBIT.X,100,float,0,NULL, 1,READ,100,[-50:50 mm]Horizontal Orbit,
TTFBPM,BPMEQM,ORBIT.Y,100,float,0,NULL, 2,READ,100,[-50:50 mm]Vertical Orbit,
TTFBPM,BPMEQM,ONLINE,1,short,1,short, 3,READ|WRITE,100,[0:1 ]Read/Set on-line status,
GetDeviceNumber
TINE_EXPORT int GetDeviceNumber(char *eqm, char *devname)
Gives the registered device number for the specified device name.
Definition: srvdbase.c:7752
illegal_read_write
@ illegal_read_write
Definition: errors.h:161
MaxNumClients
TINE_EXPORT int MaxNumClients
Determines the maximum number of clients a server will service.
Definition: srvdbase.c:7090
FeclogDepth
TINE_EXPORT int FeclogDepth
Determines the approximate size of the server's log file in lines.
Definition: syslib.c:5508
MaxNumContracts
TINE_EXPORT int MaxNumContracts
Determines the maximum number of contracts a server will service.
Definition: srvdbase.c:7089
illegal_property
@ illegal_property
Definition: errors.h:118
DTYPE
Defines a TINE data object.
Definition: tinetype.h:996
ClearAlarm
TINE_EXPORT void ClearAlarm(char *eqm, int devNr)
Instructs the local alarm server table that the given alarm is to be cleared.
Definition: almlib.c:2314
out_of_range
@ out_of_range
Definition: errors.h:119
illegal_equipment_number
@ illegal_equipment_number
Definition: errors.h:115
RegisterPropertyEx
TINE_EXPORT int RegisterPropertyEx(char *eqm, char *property, int siz, short fmt, short acc, char *dsc, int pid)
Assigns pertinent information for the specified property.
Definition: srvdbase.c:8685
RegisterEquipmentModule
TINE_EXPORT int RegisterEquipmentModule(char *expName, char *eqmName, int numdevices, int(*fcn)(char *, char *, DTYPE *, DTYPE *, short), void(*ini)(void), void(*tsk)(void), int rate, void(*exi)(void))
Registers an equipment module with the TINE server engine.
Definition: srvdbase.c:8177
dimension_error
@ dimension_error
Definition: errors.h:102
GetPropertyId
TINE_EXPORT int GetPropertyId(char *eqm, char *prpName)
Gives the associated property identifier for the given property name.
Definition: srvdbase.c:7784
RegisterDeviceName
TINE_EXPORT int RegisterDeviceName(char *eqm, char *devname, int devnr)
Assigns a device name to the specified device number.
Definition: srvdbase.c:7882
DTYPE::dArrayLength
UINT32 dArrayLength
Definition: tinetype.h:998

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