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
A Simple TINE Server (C# and VB.NET GUI)

Introduction

Below we show a fully functioning Sine Generator server written in c#. The example below was written as a GUI, although it could just as well have been a console application.

The application offers readback and control over 10 individual sine curves, which are updated at 10 Hz. It also demonstrates using a tagged structure, holding atomic property data.

The application also avoids using a configuration database (e.g. fecid.csv, exports.csv, or fec.xml) and registers all configuration data via API in the Form_Load delegate. A pure console application (without any forms) would instead place the contents of the Form_Load routine in the server contructor.

using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using tine;
using System.Windows.Forms;
// note: there are no GUI components in this example server
// add other 'buttons', labels etc. to check on the current status of things, etc.
namespace csSineServer
{
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public unsafe struct SineInfo
{
public float amplitude;
public float frequency;
public float noise;
public float phase;
public int numberCalls;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public char[] description;
};
public partial class MainServerForm : Form
{
private const int PRP_SINE = 1;
private const int PRP_AMPLITUDE = 2;
private const int PRP_FREQUENCY = 3;
private const int PRP_PHASE = 4;
private const int PRP_NOISE = 5;
private const int PRP_INFO = 6;
private const int NUM_DEVICES = 10;
private const int NUM_VALUES = (1024*8);
float[,] sinbuf = new float[NUM_DEVICES,NUM_VALUES]; // make use of a real (not jagged) double array
private SineInfo[] sineInfoTable = new SineInfo[NUM_DEVICES];
// the equipment module handler :
public Int32 sineqm(string dev, string prp, TDataType dout, TDataType din, UInt16 acc,TEquipmentModule eqm)
{
Console.WriteLine("eqm called: "+dev+" "+prp);
float fval = 0;
float[] mcarray = new float[NUM_DEVICES];
int cc;
int devnr = eqm.GetDeviceNumber(dev, prp);
int prpid = eqm.GetPropertyId(prp);
ClnInfo cln = eqm.GetCallerInformation();
String txt = "device: "+dev+", property: "+prp;
txt += Environment.NewLine + "called by : " + cln.GetUserName() + " at " + cln.GetNetworkAddr();
Console.WriteLine(txt);
switch (prpid)
{
case PRP_SINE:
if ((acc & Access.CA_WRITE) != 0) return Errors.illegal_read_write;
if ((cc = dout.PutData(sinbuf,devnr,NUM_VALUES,0)) != 0) return cc;
sineInfoTable[devnr].numberCalls++;
return 0;
case PRP_AMPLITUDE:
if (din.GetDataArrayLength() > 0)
{ /* input data => require write access */
if ((acc & Access.CA_WRITE) != Access.CA_WRITE) return Errors.illegal_read_write;
if ((cc = din.GetData(ref fval)) != 0) return cc;
if (fval < 1 || fval > 1000) return Errors.out_of_range;
sineInfoTable[devnr].amplitude = fval;
}
if (dout.GetDataArrayLength() > 0)
{ /* prepare multichannel array */
for (int i = 0; i < NUM_DEVICES; i++)
mcarray[i] = sineInfoTable[i].amplitude;
if ((cc = dout.PutData(mcarray,NUM_DEVICES,devnr)) != 0) return cc;
}
return 0;
case PRP_FREQUENCY:
if (din.GetDataArrayLength() > 0)
{ /* input data => require write access */
if ((acc & Access.CA_WRITE) != Access.CA_WRITE) return Errors.illegal_read_write;
if ((cc = din.GetData(ref fval)) != 0) return cc;
if (fval < 1 || fval > 100) return Errors.out_of_range;
sineInfoTable[devnr].frequency = fval;
}
if (dout.GetDataArrayLength() > 0)
{ /* prepare multichannel array */
for (int i = 0; i < NUM_DEVICES; i++)
mcarray[i] = sineInfoTable[i].frequency;
if ((cc = dout.PutData(mcarray,NUM_DEVICES,devnr)) != 0) return cc;
}
return 0;
case PRP_PHASE:
if (din.GetDataArrayLength() > 0)
{ /* input data => require write access */
if ((acc & Access.CA_WRITE) != Access.CA_WRITE) return Errors.illegal_read_write;
if ((cc = din.GetData(ref fval)) != 0) return cc;
if (fval < 0 || fval > 512) return Errors.out_of_range;
sineInfoTable[devnr].phase = fval;
}
if (dout.GetDataArrayLength() > 0)
{ /* prepare multichannel array */
for (int i = 0; i < NUM_DEVICES; i++)
mcarray[i] = sineInfoTable[i].phase;
if ((cc = dout.PutData(mcarray,NUM_DEVICES,devnr)) != 0) return cc;
}
return 0;
case PRP_NOISE:
if (din.GetDataArrayLength() > 0)
{ /* input data => require write access */
if ((acc & Access.CA_WRITE) != Access.CA_WRITE) return Errors.illegal_read_write;
if ((cc = din.GetData(ref fval)) != 0) return cc;
if (fval < 0 || fval > 100) return Errors.out_of_range;
sineInfoTable[devnr].noise = fval;
}
if (dout.GetDataArrayLength() > 0)
{ /* prepare multichannel array */
for (int i = 0; i < NUM_DEVICES; i++)
mcarray[i] = sineInfoTable[i].noise;
if ((cc = dout.PutData(mcarray,NUM_DEVICES,devnr)) != 0) return cc;
}
return 0;
case PRP_INFO:
if (din.GetDataArrayLength() > 0)
{ /* allow an atomic set of all relevant values this way : */
if (din.GetDataFormat() != Formats.CF_STRUCT) return Errors.illegal_format;
if (din.GetDataTag().CompareTo("SineInfo") != 0) return Errors.invalid_structure_tag;
SineInfo sinf = new SineInfo();
if ((cc = din.GetData(sinf)) != 0) return cc;
if (sinf.amplitude < 1 || sinf.amplitude > 1000) return Errors.out_of_range;
if (sinf.frequency < 1 || sinf.frequency > 100) return Errors.out_of_range;
if (sinf.phase < 0 || sinf.phase > 512) return Errors.out_of_range;
if (sinf.noise < 0 || sinf.noise > 100) return Errors.out_of_range;
sineInfoTable[devnr].amplitude = sinf.amplitude;
sineInfoTable[devnr].frequency = sinf.frequency;
sineInfoTable[devnr].phase = sinf.phase;
sineInfoTable[devnr].noise = sinf.noise;
}
if (dout.GetDataArrayLength() > 0)
{
if (dout.GetDataFormat() != Formats.CF_STRUCT) return Errors.illegal_format;
if (dout.GetDataTag().CompareTo("SineInfo") != 0) return Errors.invalid_structure_tag;
if (dout.GetDataArrayLength() > NUM_DEVICES) dout.SetDataArrayLength(NUM_DEVICES);
if ((cc = dout.PutData(sineInfoTable, NUM_DEVICES, devnr)) != 0) return cc;
}
return 0;
}
return 0;
}
void updateSine(int devnr)
{ /* used internally -> don't bother with bounds checking */
int i, k;
float f = sineInfoTable[devnr].frequency;
int n = (int)sineInfoTable[devnr].noise;
int p = (int)sineInfoTable[devnr].phase;
float a = sineInfoTable[devnr].amplitude;
Random rnd = new Random();
for (i=0; i<NUM_VALUES; i++)
{
k = (i + p) % NUM_VALUES;
sinbuf[devnr, k] = (float)(rnd.NextDouble())*n + (float)(Math.Sin(f * i * 6.2832 / (NUM_VALUES / 8)) * a);
}
return;
}
void registerStructs()
{
TTaggedStruct tts = new TTaggedStruct(sineInfoTable);
}
// the equipment module registers this routine as a 'background task'
// to be called every 100 msecs
public void sinbkg()
{ // update the sine curves
for (int i = 0; i < NUM_DEVICES; i++) updateSine(i);
if (myeqm != null) myeqm.ScheduleProperty("Sine.Sched");
}
// the equipment module registers this routine as an 'initialization task'
public void sinini()
{
for (int i = 0; i < NUM_DEVICES; i++)
{ // fill in some initial values for each of our sine curves
sineInfoTable[i].amplitude = 256;
sineInfoTable[i].frequency = 1;
sineInfoTable[i].phase = 0;
sineInfoTable[i].noise = (float)5.0;
String dsc = "Sine device " + i + " at your service";
sineInfoTable[i].description = new char[64];
Array.Copy(dsc.ToCharArray(),sineInfoTable[i].description,Math.Min(dsc.Length,64));
}
}
// the equipment module registers this routine as an 'exit task'
public void sinexi()
{ // just note that we're stopping
Console.WriteLine("server stopping");
}
public MainServerForm()
{
InitializeComponent();
}
public TEquipmentModule myeqm;
private void Form1_Load(object sender, EventArgs e)
{
registerStructs();
// to register everything via configuration database use this form:
// myeqm = new TEquipmentModule("", "SINEQM", 0, sineqm, sinini, sinbkg, 100, sinexi); <- like this will use .csv config database files
// else hardwire the 'names' via the API calls:
// register the FEC name and other information (important: portOffset must be host-wide unique)
// alternative: use a fecid.csv file (or fec.xml)
TKernel.RegisterFecInformation("CSSINE.FEC", "TEST", "TEST", "C Sharp Sine Fec", "My office", "none", "Me");
// register the equipment module and export it as 'SineServerCS'
// alternative: use the exports.csv or fec.xml to register export names and properties)
// like this is hard coded names :
myeqm = new TEquipmentModule("SineServerCS", "SINEQM", 10, sineqm, sinini, sinbkg, 100, sinexi);
// get things going:
TKernel.InitializeServer();
// register the properties (alternative: use exports.csv or fec.xml)
// first: property 'Sine' is a trace waveform which can return up to 8192 float values (no input):
TDataType dtout = new TDataType(new float[8192]);
myeqm.RegisterPropertyInformation("Sine", dtout, null, Access.CA_READ, ArrayType.AT_TRACE, 8192, "[-512:512 V]Sine Curve", PRP_SINE, "");
myeqm.RegisterPropertyInformation("Sine.Sched", dtout, null, Access.CA_READ, ArrayType.AT_TRACE, 8192, "[-512:512 V]Sine Curve", PRP_SINE, "");
// the following properties are 'multi-channel' properties
// one can read all values for the 10 sine curves at once, but only set 1 at a time:
dtout = new TDataType(new float[10]);
TDataType dtin = new TDataType(new float[1]);
myeqm.RegisterPropertyInformation("Amplitude", dtout, dtin, Access.CA_READ | Access.CA_WRITE, ArrayType.AT_CHANNEL, 10, "[-512:512 V]Sine Curve Amplitude", PRP_AMPLITUDE, "");
myeqm.RegisterPropertyInformation("Frequency", dtout, dtin, Access.CA_READ | Access.CA_WRITE, ArrayType.AT_CHANNEL, 10, "[1:20 Hz]Sine Curve Frequency", PRP_FREQUENCY, "");
myeqm.RegisterPropertyInformation("Phase", dtout, dtin, Access.CA_READ | Access.CA_WRITE, ArrayType.AT_CHANNEL, 10, "[0:6.28]Sine curve Phase", PRP_PHASE, "");
myeqm.RegisterPropertyInformation("Noise", dtout, dtin, Access.CA_READ | Access.CA_WRITE, ArrayType.AT_CHANNEL, 10, "[0:50 V]Sine curve Noise", PRP_NOISE, "");
// the property 'SineInfo' is a tagged structure
// returning all information atomically
// also allow the setting of everything via the same structure,
// but only for one sine curve at a time:
dtout = new TDataType(new SineInfo[10]);
dtin = new TDataType(new SineInfo[1]);
myeqm.RegisterPropertyInformation("SineInfo", dtout, dtin, Access.CA_READ | Access.CA_WRITE, ArrayType.AT_UNKNOWN, 10, "Sine curve Information", PRP_INFO, "");
// register some device names :
for (int i = 0; i < 10; i++)
{ // just enumerate them ...
myeqm.RegisterDeviceName("SineGen" + i, i);
}
// now start the server ...
TKernel.StartServices();
}
}
}

The same server application with the same functionality written instead in VB.NET is shown below.

Imports System.Runtime.InteropServices
Imports tine
Public Class Form1
<StructLayout(LayoutKind.Sequential, Pack:=1, CharSet:=CharSet.Ansi)> _
Public Structure SineInfo
Public amplitude As Single
Public frequency As Single
Public noise As Single
Public phase As Single
Public numberCalls As Integer
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=64)> _
Public description() As Char
End Structure
Private Const PRP_SINE = 1
Private Const PRP_AMPLITUDE = 2
Private Const PRP_FREQUENCY = 3
Private Const PRP_PHASE = 4
Private Const PRP_NOISE = 5
Private Const PRP_INFO = 6
Private Const NUM_DEVICES = 10
Private Const NUM_VALUES = (1024 * 8)
Dim sinbuf(NUM_DEVICES - 1, NUM_VALUES - 1) As Single
Dim tts As TTaggedStruct
Dim sineInfoTable(NUM_DEVICES - 1) As SineInfo
Dim myeqm As TEquipmentModule
Public Function sineqm(ByVal dev As String, ByVal prp As String, ByVal dout As TDataType, ByVal din As TDataType, ByVal acc As UShort, ByVal eqm As TEquipmentModule) As Int32
Dim fval As Single
Dim mcarray(NUM_DEVICES) As Single
Dim cc As Integer, i As Integer
Dim devnr As Integer
Dim prpid As Integer
Dim cln As ClnInfo
cln = myeqm.GetCallerInformation()
Debug.Print("called by " + cln.GetUserName() + " at " + cln.GetNetworkAddr() + vbCrLf)
devnr = eqm.GetDeviceNumber(dev, prp)
prpid = eqm.GetPropertyId(prp)
Select Case prpid
Case PRP_SINE
cc = Errors.illegal_read_write
If ((acc And Access.CA_WRITE) = 0) Then
cc = dout.PutData(sinbuf, devnr, NUM_VALUES, 0)
sineInfoTable(devnr).numberCalls = sineInfoTable(devnr).numberCalls + 1
End If
Case PRP_AMPLITUDE
If (din.GetDataArrayLength() > 0) Then
If ((acc & Access.CA_WRITE) <> Access.CA_WRITE) Then
cc = Errors.illegal_read_write
End If
cc = din.GetData(fval)
If cc <> 0 Then Exit Select
sineInfoTable(devnr).amplitude = fval
End If
If (dout.GetDataArrayLength() > 0) Then
For i = 0 To NUM_DEVICES - 1
mcarray(i) = sineInfoTable(i).amplitude
cc = dout.PutData(mcarray, NUM_DEVICES, devnr)
If cc <> 0 Then Exit Select
Next
End If
Case PRP_FREQUENCY
If (din.GetDataArrayLength() > 0) Then
If ((acc & Access.CA_WRITE) <> Access.CA_WRITE) Then
cc = Errors.illegal_read_write
End If
cc = din.GetData(fval)
If cc <> 0 Then Exit Select
sineInfoTable(devnr).frequency = fval
End If
If (dout.GetDataArrayLength() > 0) Then
For i = 0 To NUM_DEVICES - 1
mcarray(i) = sineInfoTable(i).frequency
cc = dout.PutData(mcarray, NUM_DEVICES, devnr)
If cc <> 0 Then Exit Select
Next
End If
Case PRP_PHASE
If (din.GetDataArrayLength() > 0) Then
If ((acc & Access.CA_WRITE) <> Access.CA_WRITE) Then
cc = Errors.illegal_read_write
End If
cc = din.GetData(fval)
If cc <> 0 Then Exit Select
sineInfoTable(devnr).phase = fval
End If
If (dout.GetDataArrayLength() > 0) Then
For i = 0 To NUM_DEVICES - 1
mcarray(i) = sineInfoTable(i).phase
cc = dout.PutData(mcarray, NUM_DEVICES, devnr)
If cc <> 0 Then Exit Select
Next
End If
Case PRP_NOISE
If (din.GetDataArrayLength() > 0) Then
If ((acc & Access.CA_WRITE) <> Access.CA_WRITE) Then
cc = Errors.illegal_read_write
End If
cc = din.GetData(fval)
If cc <> 0 Then Exit Select
sineInfoTable(devnr).noise = fval
End If
If (dout.GetDataArrayLength() > 0) Then
For i = 0 To NUM_DEVICES - 1
mcarray(i) = sineInfoTable(i).noise
cc = dout.PutData(mcarray, NUM_DEVICES, devnr)
If cc <> 0 Then Exit Select
Next
End If
Case PRP_INFO
If (din.GetDataArrayLength() > 0) Then
If (din.GetDataFormat() <> Formats.CF_STRUCT) Then
cc = Errors.illegal_format
Exit Select
End If
If (din.GetDataTag().CompareTo("SineInfo") <> 0) Then
cc = Errors.invalid_structure_tag
End If
Dim sinf As SineInfo
sinf = New SineInfo()
cc = din.GetData(sinf)
If cc <> 0 Then Exit Select
sineInfoTable(devnr).amplitude = sinf.amplitude
sineInfoTable(devnr).frequency = sinf.frequency
sineInfoTable(devnr).phase = sinf.phase
sineInfoTable(devnr).noise = sinf.noise
End If
If (dout.GetDataArrayLength() > 0) Then
If (dout.GetDataFormat() <> Formats.CF_STRUCT) Then
cc = Errors.illegal_format
Exit Select
End If
If (dout.GetDataTag() <> "SineInfo") Then
cc = Errors.invalid_structure_tag
End If
If (dout.GetDataArrayLength() > NUM_DEVICES) Then dout.SetDataArrayLength(NUM_DEVICES)
cc = dout.PutData(sineInfoTable, NUM_DEVICES, devnr)
End If
Case Else
cc = Errors.illegal_property
End Select
sineqm = cc
End Function
Public Sub updateSine(ByVal devnr As Integer)
Dim i As Integer, k As Integer
Dim f As Single, n As Single, p As Single, a As Single
f = sineInfoTable(devnr).frequency
n = sineInfoTable(devnr).noise
p = sineInfoTable(devnr).phase
a = sineInfoTable(devnr).amplitude
For i = 0 To NUM_VALUES - 1
k = (i + p) Mod NUM_VALUES
sinbuf(devnr, k) = (Rnd()) * n + (Math.Sin(f * i * 6.2832 / (NUM_VALUES / 8)) * a)
Next
End Sub
Public Sub sinbkg()
Dim i As Integer
For i = 0 To NUM_DEVICES - 1
Call updateSine(i)
Next
End Sub
Public Sub sinini()
Dim i As Integer, k As Integer
Dim dsc As String
Dim tdt As TDataType
For i = 0 To NUM_DEVICES - 1
sineInfoTable(i).amplitude = 256
sineInfoTable(i).frequency = 1
sineInfoTable(i).phase = 0
sineInfoTable(i).noise = 5.0
ReDim sineInfoTable(i).description(63)
dsc = "Sine device " + Str(i) + " at your service"
For k = 0 To Len(dsc) - 1
sineInfoTable(i).description(k) = dsc.Chars(k)
Next
Next
End Sub
Public Sub sinexi()
Console.WriteLine("server stopping")
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
TKernel.SetAppDate(DateTime.Now)
TKernel.SetAppVersion(1, 0, 1)
TKernel.RegisterFecInformation("VBSINE.FEC", "TEST", "TEST", "VB Dot Net Sine Server Test", "Here", "none", "me")
TKernel.InitializeServer()
tts = New TTaggedStruct(sineInfoTable)
myeqm = New TEquipmentModule("/TEST/SineServer.VB", "SINEQM", 10, AddressOf sineqm, AddressOf sinini, AddressOf sinbkg, 100, AddressOf sinexi)
Dim dtout As TDataType
Dim dtin As TDataType
dtout = New TDataType(NUM_VALUES, Formats.CF_FLOAT, "")
myeqm.RegisterPropertyInformation("Sine", dtout, Nothing, Access.CA_READ, ArrayType.AT_TRACE, NUM_VALUES, "[-1000:1000 V][0:1000 Hz]Sine curve", PRP_SINE, Nothing)
dtout = New TDataType(NUM_DEVICES, Formats.CF_FLOAT, "")
dtin = New TDataType(1, Formats.CF_FLOAT, "")
myeqm.RegisterPropertyInformation("Amplitude", dtout, dtin, Access.CA_READ & Access.CA_WRITE, ArrayType.AT_CHANNEL, NUM_DEVICES, "[-1000:1000 V]Sine curve Amplitude", PRP_AMPLITUDE, Nothing)
myeqm.RegisterPropertyInformation("Frequency", dtout, dtin, Access.CA_READ & Access.CA_WRITE, ArrayType.AT_CHANNEL, NUM_DEVICES, "[1:100 Hz]Sine curve Frequency", PRP_FREQUENCY, Nothing)
myeqm.RegisterPropertyInformation("Phase", dtout, dtin, Access.CA_READ & Access.CA_WRITE, ArrayType.AT_CHANNEL, NUM_DEVICES, "[0:10]Sine curve Phase", PRP_PHASE, Nothing)
myeqm.RegisterPropertyInformation("Noise", dtout, dtin, Access.CA_READ & Access.CA_WRITE, ArrayType.AT_CHANNEL, NUM_DEVICES, "[0:100 V]Sine curve Noise", PRP_NOISE, Nothing)
dtout = New TDataType(NUM_DEVICES, Formats.CF_STRUCT, tts.GetTag())
dtin = New TDataType(1, Formats.CF_STRUCT, tts.GetTag())
myeqm.RegisterPropertyInformation("Info", dtout, dtin, Access.CA_READ & Access.CA_WRITE, ArrayType.AT_CHANNEL, NUM_DEVICES, "Sine curve Information", PRP_INFO, Nothing)
TKernel.StartServices()
End Sub
End Class
ClnInfoStruct
Client Information Structure used in GetCallerInformation.
Definition: srvcore.h:2485

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