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;
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];
private SineInfo[] sineInfoTable = new SineInfo[NUM_DEVICES];
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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);
}
public void sinbkg()
{
for (int i = 0; i < NUM_DEVICES; i++) updateSine(i);
if (myeqm != null) myeqm.ScheduleProperty("Sine.Sched");
}
public void sinini()
{
for (int i = 0; i < NUM_DEVICES; i++)
{
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));
}
}
public void sinexi()
{
Console.WriteLine("server stopping");
}
public MainServerForm()
{
InitializeComponent();
}
public TEquipmentModule myeqm;
private void Form1_Load(object sender, EventArgs e)
{
registerStructs();
TKernel.RegisterFecInformation("CSSINE.FEC", "TEST", "TEST", "C Sharp Sine Fec", "My office", "none", "Me");
myeqm = new TEquipmentModule("SineServerCS", "SINEQM", 10, sineqm, sinini, sinbkg, 100, sinexi);
TKernel.InitializeServer();
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, "");
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, "");
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, "");
for (int i = 0; i < 10; i++)
{
myeqm.RegisterDeviceName("SineGen" + i, i);
}
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