Main Page | Features | Central Services | csv-Files | Types | Transfer | Access | API-C | API-.NET | API-Java | Examples | Downloads
page generated on 05.05.2024 - 04:45
A Simple TINE Java Server (console)

Introduction

TINE console server applications follow the usual TINE server dichotomy, where the developer's task is to provide an equipment module which handles requests from the client side as well as the internal local archive and local alarm servers.

The developer can also make use of the associated background tasks for dealing with local server i/o.

The example below is a simple java server code module which offers a sine curve to calling applications.

This example makes extensive use of the standard TINE startup configuration .csv files, in particular: fecid.csv, exports.csv, and <local-name>-devices.csv to register all equipment module names, property names, and device names. The history.csv files and almwatch.csv files can also be used to provide local histories and to catch certain types of alarms.

Most the comments are inside the example code, which consists of three modules: 1)a Sine Device Class which inherits from a TDevice and is used to instantiate the devices served by the device server, 2) a Sine Equipment Module Class which inherites from a TEquipmentModule Class and provides the primary server interface, and 3) a Sine Device Server Class which constitutes the device server.

Although it is possible to encode all relevant names (properties, devices, etc.) inside these modules, it is much easier for the purposes of this illustration to make use of the TINE startup configuration files 'fecid.csv' to provide the fec name and other information, the 'exports.csv' file to provide the exported device server name and the exported properties, and a 'devices.csv' file to register the individual device names.

The Sine Device Class

This is an example for a device. The device periodically produces an array of values in its update() method. The device has properties which can be read and set via set/get methods.

package de.desy.tine.example.sine;
import de.desy.tine.server.devices.*;
public class SineDevice extends TDevice
{
public static final int DATASIZE = 1024;
public static final double PHASEDIFF = 2 * Math.PI / 100.0;
private double frequency = 1;
private double amplitude = 1;
private double[] myData;
private double phase = 0.0f;
// Use this constructor if you intend on working with device (i.e. equipment)
// numbers in your code and you use a standard 'devices.csv' file
// to register the exported device names.
public SineDevice(int newNumber)
{
super(newNumber);
myData = new double[DATASIZE];
}
// Use this constructor if you 1) want to explicitly assign the device names inside
// your code or 2) you are reading in this information via your own database
// (and not via 'devices.csv') and hence need to assign the device
// names to device numbers in code.
public SineDevice(String newName,int newNumber)
{
super(newName,newNumber);
myData = new double[DATASIZE];
}
public double getFrequency()
{
return frequency;
}
public void setFrequency(double frequency)
{
this.frequency = frequency;
}
public double getAmplitude()
{
return amplitude;
}
public void setAmplitude(double amplitude)
{
this.amplitude = amplitude;
}
public double[] getSine()
{
return myData;
}
private void incrementPhase()
{
double phasediff = Math.PI / 100.0;
phase += phasediff;
if (phase + phasediff > 2.0 * Math.PI) phase = 0.0f;
}
// Called in the background task.
public void update()
{
clearAlarm(512);
incrementPhase();
for (int i = 0; i < 1024; i++)
myData[i] = amplitude * Math.sin(phase + frequency * 6.28 * ((double) i / 1024.0));
if (amplitude > 100) setAlarm(512); // amplitude too high !
}
}

The Sine Equipment Module Class

Example equipment module. The equipment module maps the device properties to TINE properties.

Notes: The routine registerProperties(): The principal property registeration occurs via the 'exports.csv' configuration file. In this case the property 'handlers' need to be attached to the registered properties. An alternative would be the use the registerProperty(TExportProperty, TPropetyHandler) method of TEquipmentModule. In this case you would have to supply all property information inside an instance of TExportProperty prior to making the call. Under some circumstances it might be desireable to keep the property information in an external database (such as 'exports.csv') rather than to hard code it in this manner.

Property Handler examples: Have a look at the examples for handling requests for properties "Amplitude" and "Frequency". In one case ("Frequency") the the entire request is passed to a call routine which handles 'write' and 'read' requests collectively. The other case ("Amplitude") splits the 'write' and 'read' requests into two separate routines. The differences between the two is by and large cosmetic. It is generally useful to allow atomic 'write-read' requests along with individual 'writes' and 'reads'.

Such equipment module routines should be to a large extent generated from a wizard, so that your only task might be to fill in or expand routines such as writeAmplitude to suit your needs.

package de.desy.tine.example.sine;
import de.desy.tine.dataUtils.*;
import de.desy.tine.definitions.*;
import de.desy.tine.server.devices.*;
import de.desy.tine.server.equipment.*;
import de.desy.tine.server.properties.*;
import de.desy.tine.types.*;
public class SineEquipmentModule extends TEquipmentModule
{
boolean wrapMultiChannelArrays = true;
TDeviceList myDeviceSet;
// Wrapper which casts to sineDevice
private SineDevice findDevice(String deviceName)
{
if (myDeviceSet == null) return null;
return (SineDevice) myDeviceSet.getDevice(deviceName);
}
// Constructor. Supplies a specific local name setting.
public SineEquipmentModule(String localName, SineDevice[] devices)
{
super(localName);
registerDevices(devices);
registerProperties();
myDeviceSet = super.getDeviceList();
}
// Constructor. Attempts to determine a suitable local name.
// The local name used will be the first 6 characters of the class name,
// in this case 'SineEq'. The local name is required to be unique only within
// a given server process. Thus, if another TEquipmentModule is to be
// registered with the same factory, it should have a class name whose initial
// 6 characters differ from this one.
public SineEquipmentModule(SineDevice[] devices)
{
super();
registerDevices(devices);
registerProperties();
myDeviceSet = super.getDeviceList();
}
private int readString(String devName, TDataType dout)
{
return dout.putData(new NAME16(devName));
}
// This Reads the sine values of a device. Called by PropertyHandler.
private int readSine(String devName, TDataType dout)
{
SineDevice device = findDevice(devName);
if (device == null) return TErrorList.device_not_connected;
return dout.putData(device.getSine());
}
public int writeAmplitude(String devName, TDataType din)
{ // CLIENT WANTS TO SET AMPLITUDE
int cc = 0;
SineDevice theDevice = null;
if (din.getArrayLength() != 1) return TErrorList.dimension_error;
double[] input = new double[1];
theDevice = (SineDevice) myDeviceSet.getDevice(devName);
if (theDevice == null) return TErrorList.illegal_equipment_number;
if ((cc=din.getData(input)) != 0) return cc;
theDevice.setAmplitude(input[0]);
return 0;
}
// Reads the amplitude of a device or set of devices.
// If 'wrapMultiChannelArrays' is true, the caller will can start at
// any element of his choosing and receive a full array as output
// (regardless of the datalength requested and the starting point),
// otherwise the array might be truncated if the number of requested
// elements exceeds the number of array elements remaining.
public int readAmplitude(String devName, TDataType dout)
{
int cc = 0;
SineDevice theDevice;
int nret = dout.getArrayLength();
if (nret < 1) return TErrorList.dimension_error;
int ndv = myDeviceSet.getNumberOfDevices();
int dv = myDeviceSet.getDeviceNumber(devName);
if (dv < 0 || dv >= ndv) return TErrorList.illegal_equipment_number;
if (!wrapMultiChannelArrays && dv + nret > ndv) nret = ndv - dv;
double[] output = new double[nret];
for (int i=0; i<nret; i++)
{
theDevice = (SineDevice) myDeviceSet.getDevice(dv++ % ndv);
output[i] = theDevice.getAmplitude();
}
cc = dout.putData(output);
return cc;
}
// This property handler reads or sets the frequency of one device, or reads
// the frequencies of a subset of devices. This call method demonstrates the
// read of a multi-channel array, where each element of the array refers to a
// registered device. Note the (optional) end-wrapping, allowing the caller to
// retrieve the entire array beginning at an element of his choosing.
// If 'wrapMultiChannelArrays' is true, the caller will can start at
// any element of his choosing and receive a full array as output
// (regardless of the datalength requested and the starting point),
// otherwise the array might be truncated if the number of requested
// elements exceeds the number of array elements remaining.
private int callFrequency(String devName, TDataType dout, TDataType din, TAccess devAccess)
{
int cc = 0;
SineDevice theDevice;
if (devAccess.isWrite())
{ // CLIENT WANTS TO SET FREQUENCY (allow single-channel write)
double[] input = new double[1];
if (din.getArrayLength() != 1) return TErrorList.dimension_error;
theDevice = (SineDevice) myDeviceSet.getDevice(devName);
if (theDevice == null) return TErrorList.illegal_equipment_number;
if ((cc=din.getData(input)) != 0) return cc;
theDevice.setFrequency(input[0]);
}
if (devAccess.isRead())
{ // CLIENT WANTS TO READ (allow multi-channel read)
int nret = dout.getArrayLength();
if (nret < 1) return TErrorList.dimension_error;
int ndv = myDeviceSet.getNumberOfDevices();
int dv = myDeviceSet.getDeviceNumber(devName);
if (dv < 0 || dv >= ndv) return TErrorList.illegal_equipment_number;
if (!wrapMultiChannelArrays && dv + nret > ndv) nret = ndv - dv;
double[] output = new double[nret];
for (int i=0; i<nret; i++)
{
theDevice = (SineDevice) myDeviceSet.getDevice(dv++ % ndv);
output[i] = theDevice.getFrequency();
}
cc = dout.putData(output);
}
return cc;
}
private void registerProperties()
{ // all property information (except the handlers) from exports.csv file
getExportInformationFromFile();
// attach the property handlers ...
attachPropertyHandler("SINE",new TPropertyHandler()
{
protected int call(String devName, TDataType dout, TDataType din, TAccess devAccess)
{
return readSine(devName, dout);
}
});
attachPropertyHandler("Amplitude",new TPropertyHandler()
{
protected int call(String devName, TDataType dout, TDataType din, TAccess devAccess)
{
int rc = TErrorList.illegal_read_write;
if (devAccess.isWrite())
{
rc = writeAmplitude(devName, din);
if (rc != 0) return rc;
}
if (devAccess.isRead())
{
rc = readAmplitude(devName, dout);
}
return rc;
}
});
attachPropertyHandler("Frequency",new TPropertyHandler()
{
protected int call(String devName, TDataType dout, TDataType din, TAccess devAccess)
{
return callFrequency(devName, dout, din, devAccess);
}
});
attachPropertyHandler("ThisDevice",new TPropertyHandler()
{
protected int call(String devName, TDataType dout, TDataType din, TAccess devAccess)
{
return readString(devName, dout);
}
});
}
}

The Sine Device Server Class

This is an example of a TINE-based server. The device server consists of 10 devices. Each device generates data of a propagating sine wave. For each device, the amplitude and the frequency of the sine wave can be set and read.

package de.desy.tine.example.sine;
import java.util.LinkedList;
import de.desy.tine.server.equipment.*;
public class SineDeviceServer
{
private static SineEquipmentModule sineEqpModule;
private static TEquipmentBackgroundTask sineBkgFcn;
private TEquipmentModuleFactory thisEqmFactory;
private LinkedList sineDeviceSet = new LinkedList();
// In the constructor we instantiate our devices and
// initialize the device server.
public SineDeviceServer()
{
initializeDevices(); // should create a device list (eqm not yet known !)
initializeDeviceServer(); // will create an eqm, passing a device list
// optional: spit these out just to confirm they have been registered ...
sineEqpModule.dumpProperties();
sineEqpModule.dumpDevices();
}
private void initializeDevices()
{
// create the devices and add them to a device set
for (int i = 0; i < 10; i++)
{
// add new devices; assign them only a device number :
sineDeviceSet.add(new SineDevice(i));
// The device names will be assigned through the configuration
// file devices.csv located in this case in subdirectory 'SineEq'
// Alternative: fill in via your own database or just fake it:
// e.g. sineDeviceSet.add(new SineDevice("SINE_DEV_" + i,i));
}
}
private void initializeDeviceServer()
{
// Create Equipment Module(s) (this example has only one)
sineEqpModule = new SineEquipmentModule((SineDevice[])sineDeviceSet.toArray(new SineDevice[0]));
//Create Background Task(s) (hardware IO, middle-layer activities, etc.)
sineBkgFcn = new TEquipmentBackgroundTask()
{ // must implement the 'call' method :
public void call()
{
for (int i=0; i<sineDeviceSet.size(); i++)
((SineDevice)sineDeviceSet.get(i)).update();
}
};
// how often should the background task be called :
sineBkgFcn.setBackgroundTaskInterval(200); // msec
}
public void activate()
{
// record a reference to the equipment module factory (all modules will use this one)
thisEqmFactory = sineEqpModule.getTEqmFactory();
// add the background task(s) to the factory
if (sineBkgFcn != null)
thisEqmFactory.addEquipmentBackgroundTask(sineBkgFcn);
// initialize the FEC (starts all services, reads configuration databases, etc.)
thisEqmFactory.systemInit(); // initialize the factory
// alternative: thisEqmFactory.systemInit(myFecName, myTinePort) hardcodes
// the FECNAME instead of using fecid.csv
thisEqmFactory.systemWait(-1); // wait here forever ... (or loop here and check command line?)
}
public static void main(String[] args)
{
// create the device server; export name read from exports.csv file
SineDeviceServer theServer = new SineDeviceServer();
// optional: sineEqpModule.setExportName(myExportName) will hardcode
// the exported device server name instead of reading from exports.csv
// Now start serving ...
theServer.activate();
}
}

Before trying to run this example, make sure the necessary startup files are present in the fec.home location (see the tine.properties Property File).

The fec name is obtained from:

Fec_Name,Context,Export_Name,Port_Offset,SubSystem,Description,Location,Hardware,Responsible
JSINESRV,TEST,JAVASINE, 6,TST,Sine server,Bldg 30 Rm 300,None,"P.Duval,J.Q.Public"

The exported device server name as well as the property names are obtained from:

EXPORT_NAME,LOCAL_NAME,PROPERTY,PROPERTY_ID,ACCESS,FORMAT,PROPERTY_SIZE,INFORMAT,PROPERTY_INSIZE,NUM_MODULES,DESCRIPTION,REDIRECTION
JAVASINE,SineEq,SINE,1,READ,float,1024,,,100,[0:1 bozos !LIN][1000:2024 dinges]Sine Curve,
JAVASINE,SineEq,TEST,2,READ,float.CHANNEL,1024,,,100,[0:1000 bozos]Test Curve,
JAVASINE,SineEq,ECHO,3,READ,long,1024,long,1024,100,[0:1000 bozos]echo input,
JAVASINE,SineEq,MODE,5,READ|WRITE,short,1,short,1,100,[0:1 bozos]noise or travelling,
JAVASINE,SineEq,STRUCTTEST,6,READ|WRITE,struct,1,,,100,struct test,
JAVASINE,SineEq,Amplitude,7,READ|WRITE,double,1,double,1,100,[0:1000 bozos]sine amplitude,
JAVASINE,SineEq,Frequency,8,READ|WRITE,double,1,double,1,100,[0:100 Hz]sine frequency,
JAVASINE,SineEq,HURRICANE,10,READ,float,1024,,,100,[0:1 bozos !LIN][1000:2024 dinges]Sine Curve,SINE[SINE]
JAVASINE,SineEq,NOTREADY,11,READ,float,1,,,100,float value,
JAVASINE,SineEq,ThisDevice,12,READ,NAME16,1,,,100,string value,

The exported device names are obtained from:

DEVICE_NAME,DEVICE_NUMBER,REDIRECTION
SINEDEV_0,0,
SINEDEV_1,1,DOSSINE
SINEDEV_2,2,
SINEDEV_3,3,
SINEDEV_4,4,
SINEDEV_5,5,SINE
SINEDEV_6,6,
SINEDEV_7,7,
SINEDEV_8,8,
SINEDEV_9,9,
NAME16
Defines a TINE 16-character fixed-length string data object.
Definition: tinetype.h:242
TErrorList
TErrorList
Definition: errors.h:74

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