Main Page | Features | Central Services | csv-Files | Types | Transfer | Access | API-C | API-VB/ActiveX | API-Java | Examples | Downloads
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:

srvWizard.jpg

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:
        return illegal_property;
  }
}

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;
  MaxNumContracts = 150;
  MaxNumClients = 50;
  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,

Generated for TINE API by  doxygen 1.5.8