Main Page | Features | Central Services | csv-Files | Types | Transfer | Access | API-C | API-.NET | API-Java | Examples | Downloads
page generated on 18.05.2024 - 04:45
Accessing Your Server in VB

Basic Non-GUI Calls

We'll begin by getting a set of data from our Workshop server and displaying the output. First, no ACOP! Start Visual Basic and create a new project. Add the following two modules to the project: tine.bas and utils.bas. You'll find them under \mkinthera1\hera\projects\service\vb\forms if you're at DESY, otherwise in the ..\Windows\apps32\common area of where your tine installation is.

Now add a label and a combo box to form1. Your application should look something like this:

Now double click on form1 to land in the form_load routine and add the following code.

Private Sub Form_Load()
Dim devnams() As String
Dim ndevnames As Integer
Dim rc As Integer, i As Integer
rc = GetEquipmentDeviceNames("/WORKSHOP/WKSineGen", devnams(), ndevnames)
If rc <> 0 Then
MsgBox "Get Device Names Error : " + RPCERROR(rc)
Else
For i = 0 To ndevnames - 1
Combo1.AddItem devnams(i)
Next
Combo1.ListIndex = 0
End If
End Sub

This will simple retrieve the device list from the server and fill in our combo box. By setting the list index to the first item in the combo box, we'll trigger the click event of the combo box. Go there and add the following code (Note: by double clicking on the combo box, you'll land in the change event handler by default - if there are no others - so make sure you change the event handle to that for the click event!).

Private Sub Combo1_Click()
Dim rc As Integer
Dim dout As DBLK, din As DBLK
Dim t As Single
dout.dArrayLength = 1
dout.dFormat = CF_FLOAT
din.dArrayLength = 0
din.dFormat = CF_NULL
rc = T_EXECLINK("/WORKSHOP/WKSineGen/" + Combo1.text, "Temperature", dout, t, din, 0, CA_READ, 1000)
If rc <> 0 Then
Label1.Caption = RPCERROR(rc)
Else
Label1.Caption = str(t) + " 'C"
End If
End Sub

Here we simply call the Visual Basic version of ExecLink (T_EXECLINK), which takes two data objects of type DBLK to specify the output data (from the server) and the input data (to the server). As output, we want one single (float) value, and as input we don't want to send anything to the server. T_EXECLINK() is a synchronous call and is completely finished when the call returns.

You can now run this simple program and see that you can retrieve the temperature for the selected item in the combo box.

Is is possible to monitor the temperature without resorting to ACOP? Yes! Let's try this out. Add a new module to your application. In this case, we'll need a global variable to hold the temperature so add a variable sineTemperature to the module and while we're at it, let's also add a global variable to hold the link id of the monitor link we're about to start.

Global sineTemperature As Single
Global sineLinkId As Long

Now add a callback Sub routine to the same module:

Sub cbTemperature(ByVal id As Long, ByVal rc As Long)
If rc <> 0 Then
Form1.Label1.Caption = RPCERROR(rc)
Else
Form1.Label1.Caption = str(sineTemperature) + " 'C"
End If
End Sub

All that remains now is to change our synchronous link to get the current temperature to an asynchronous monitor. So go back to the Combo box's click event and change a few things. We'll want to change the synchronous call to T_EXECLINK() to the asynchronous call T_ATTACHLINK(), which returns immediately and returns a link handle instead of a return code. There's one tiny problem in the Visual Basic world, namely T_ATTACHLINK takes two DTYPE objects as input parameters instead of DBLK objects as in the case for T_EXECLINK. So change the data type for dout and din to DTYPE. The difference is an extra field specifying the address of the data element which should be filled in by the link. This data element had better be global! It needs to be available to the callback routine. So instead of a simple variable 't' on the stack use the sineTemperature variable we created in our .bas module.

Your code inside the combo's click event should look something like:

Private Sub Combo1_Click()
Dim rc As Integer
Dim dout As DTYPE, din As DTYPE
Dim t As Single
T_CLOSELINK sineLinkId
dout.dArrayLength = 1
dout.dFormat = CF_FLOAT
dout.ddata = CADDR(sineTemperature)
din.dArrayLength = 0
din.dFormat = CF_NULL
din.ddata = 0
sineLinkId = T_ATTACHLINK("/WORKSHOP/WKSineGen/" + Combo1.text, "Temperature", dout, din, CA_READ, 1000, AddressOf cbTemperature, CM_POLL)
If sineLinkId < 0 Then
rc = -sineLinkId
Label1.Caption = RPCERROR(rc)
End If
End Sub

Note that we close the existing link (if any) when we enter the routine (otherwise we'd have numerous active links attaching to the same data element.

We said that T_ATTACHLINK returns immediately and this is generally true. But if you want to delay execution of your program until the first data set returns you can. Add the CM_WAIT flag to the polling mode argument and see what happens. To test this you can modify the T_ATTACHLINK in your click event to something like:

sineLinkId = T_ATTACHLINK("/WORKSHOP/WKSineGen/" + Combo1.text, "Temperature", dout, din, CA_READ, 1000, AddressOf cbTemperature, CM_POLL + CM_WAIT)
If sineLinkId < 0 Then
rc = -sineLinkId
Label1.Caption = RPCERROR(rc)
Else
MsgBox "initial data: " + str(sineTemperature) + " 'C"
End If

So this is a way of access tine servers in Visual Basic without using ACOP. You might want to do this if you're not interested in displaying the data anyway. Note two important things: Using the AddressOf function requires your Sub routine to be in a .bas module otherwise it will not work; and do NOT try to put a break point in your callback routine (unless you want to hang Visual Basic).

We'll begin by getting a set of data from our Workshop server and displaying the output. As Visual Basic (practically by definition) implies GUI applications, we'll skip command line applications and go right to using ACOP as a general purpose displayer.

Visual Basic and ACOP

Let's now proceed to a standard Visual Basic GUI application which will display the data from our test server. Start Visual Basic, and create a new project. Add tine.bas and utils.bas to the project. Next, add Acop to the toolkit and place an acop on Form1. So far, no code, and your design-time form probably looks like:

You can change the name of the acop control to something more meaningful than the default 'Acop1', for instance 'SineCurve' or something. But that's up to you. Now right click on the acop to call up the ActiveX property pages. We're going to attach a link to your server to the acop chart displayer. So first check the 'query EGU' check box. Now browse for your server, starting at the top, i.e. select context "WORKSHOP" and work your way down to property "Sine". Change the 'Mode' to "POLL" and click on the 'Apply' Button. It should look something like:

Now click on 'OK'.

Now let's add a couple of lines of code to get us going. Double click on Form1 to land in the Form_load routine.
We'll want to start a link here, but before we do that make a global variable to hold a sine curve array. This should be a Single array of dimension 1024. For instance:

Dim s(1023) As Single

Now in Form_load call the AttachLink method of the acop control and attach the link to the global variable you created.

SineCurve.AttachLink s

Double click on the acop control to call up the Receive Event, and add the following code (assuming you've renamed your control to SineCurve).

SineCurve.ClearScreen
SineCurve.Draw s

Now run this, and it should work.

Note a couple of things right off the bat. One: We've indulged in optomistic programming all the way. The Acop methods usually have return codes and the Receive event has a status code and these might tell us what went wrong if something failed. So checking whether a call to AttachLink returned a valid link handle (> 0) is a good idea! Two: The 'query EGU' mechanism got it right for the y-axis, namely the maximum and minimum were set according to what the server said belong with property 'Sine', and the y-axis Units are also okay. But that's all it did! You should revisit the property pages and set the x-maximum to 1024 and the x-axis label to 'usec' or what ever you'd like.

Add a global variable to hold the link handle returned from the call the AttachLink and check that it's valid following the call to AttachLink. Likewise inside the Receive Event check the StatusCode variable to see if the link is still valid. Note that the Receive event will be fired upon either incoming data or non-zero status code. Your project should now look something like:

Option Explicit
Dim s(1023) As Single
Dim idSineCurve As Long
Private Sub Form_Load()
idSineCurve = SineCurve.AttachLink(s)
If idSineCurve < 0 Then
SineCurve.PrintText SineCurve.Status
End If
End Sub
Private Sub SineCurve_Receive(ByVal LinkIndex As Long, ByVal StatusCode As Long)
SineCurve.ClearScreen
SineCurve.ClearText
If StatusCode Then
SineCurve.PrintText SineCurve.Status
End If
SineCurve.Draw s
End Sub

Selecting a Device

Try this out and you should see no difference than before. But let's now add the Combo box we had in our earlier application and put the same code as before in the form_load routine. But now move the 'AttachLink' to the combo box's click event.

So form_load should now look like:

Private Sub Form_Load()
Dim devnams() As String
Dim ndevnames As Integer
Dim rc As Integer, i As Integer
rc = GetEquipmentDeviceNames("/WORKSHOP/WKSineGen", devnams(), ndevnames)
If rc <> 0 Then
MsgBox "Get Device Names Error : " + RPCERROR(rc)
Else
For i = 0 To ndevnames - 1
Combo1.AddItem devnams(i)
Next
Combo1.ListIndex = 0
End If
End Sub

We'll also want to attach a link to the selected device so let's modify the code to start the link (which you have moved to the combo's click event) to do this. To help us do this, we can make use of an acop feature. Namely all of the device access information is contained as persistent data inside the acop object. This made like easy at the beginning, but is now a little bothersome. So right-click on the acop control again to get the proptery pages, and click on the 'To Clipboard' button. Then close the property pages and enter the combo box's click event and paste the clipboad into the event. Then you can modify the 'DeviceName' field to represent the current combo box text. Also make sure you close any existing links prior to starting a new one.

Your click event code should look something like:

Private Sub Combo1_Click()
SineCurve.CloseLink
SineCurve.DeviceContext = "WORKSHOP"
SineCurve.DeviceGroup = "WKSineGen"
SineCurve.DeviceName = Combo1.text
SineCurve.DeviceProperty = "Sine"
SineCurve.AccessMode = "POLL"
SineCurve.AccessRate = 1000
SineCurve.AccessProtocol = "TINE"
idSineCurve = SineCurve.AttachLink(s)
If idSineCurve < 0 Then
SineCurve.PrintText SineCurve.Status
End If
End Sub

Try this out. If you change the devices, you probably won't see much of a difference unless you modify the amplitude of one of the devices (you can use the instant client to do this for now).

Grouping Elements in a Structure

Now let's add information about the temperature and the amplitude of the selected device.

Let's modify the data element containing our sine curve trace to be a user-defined Type, containing fields for the trace, the amplitude and the temperature. Something like:

Option Explicit
Private Type SineCurveType
trace(1023) As Single
temperature As Single
amplitude As Single
End Type
Dim s As SineCurveType
Dim idSineCurve As Long
Dim idTemperature As Long
Dim idAmplitude As Long

After you do this, you'll need to go back and change references to 's' to refere instead to 's.trace'.

Now add two labels to your form and change to caption of one to 'Amplitude' and of the other to 'Temperature'. Then add two Text Boxes next to these labels. You can change the names of the text boxes to something more meaningful than 'Text1' and 'Text2' if you like, that's up to you.

Your form probably looks something like:

Now, inside the combo box's click event add links (using the same acop!) to get the Amplitude and the Temperature of the chosen device. One other thing: We only want to process one receive event when everything is there, so see the 'grouped' property of the SineCurve acop to 'true'. Note that as the data all come from the same server, everything will be packed together and arrive together anyway.

The click event should look something like:

Private Sub Combo1_Click()
SineCurve.CloseLink
SineCurve.DeviceContext = "WORKSHOP"
SineCurve.DeviceGroup = "WKSineGen"
SineCurve.DeviceName = Combo1.text
SineCurve.DeviceProperty = "Sine"
SineCurve.AccessMode = "POLL"
SineCurve.AccessRate = 1000
SineCurve.AccessProtocol = "TINE"
SineCurve.Grouped = True
idSineCurve = SineCurve.AttachLink(s.trace)
If idSineCurve < 0 Then
SineCurve.PrintText SineCurve.Status
End If
SineCurve.DeviceProperty = "Amplitude"
idAmplitude = SineCurve.AttachLink(s.amplitude)
If idAmplitude < 0 Then
SineCurve.PrintText SineCurve.Status
End If
SineCurve.DeviceProperty = "Temperature"
idTemperature = SineCurve.AttachLink(s.temperature)
If idTemperature < 0 Then
SineCurve.PrintText SineCurve.Status
End If
End Sub


Now revisit the receive event of the acop control and process the new data sets so that the two new text boxes are filled in.

Private Sub SineCurve_Receive(ByVal LinkIndex As Long, ByVal StatusCode As Long)
SineCurve.ClearScreen
SineCurve.ClearText
If StatusCode Then
SineCurve.PrintText SineCurve.Status
End If
SineCurve.Draw s.trace
Text1.text = str(s.temperature)
Text2.text = str(s.amplitude)
End Sub


Try your application out now. Everything should be fine, but you won't still see much of a difference in the displays until you change some of the amplitude settings. So let's build the ability to do this into our application.

Changing a Setting

Add a button onto the form next to the Amplitude text box display and change its label to 'Set'. In the click event of the button add code something like the following:

Private Sub Command1_Click()
Dim newval As Single
Dim rc As Integer
newval = Val(InputBox("Input the new Amplitude Setting:", "Sine Curve Amplitude", Text2.text))
SineCurve.DeviceProperty = "Amplitude"
SineCurve.DeviceName = Combo1.text
SineCurve.AccessMode = "WRITE"
rc = SineCurve.Execute(newval, , , newval)
If rc <> 0 Then
MsgBox "Set amplitude failed : " + SineCurve.Status
End If
End Sub


If you run your application now, you should be able to set the amplitude for the selected device.

Displaying the Local History

The next fun thing to do is to add a local history display to our application. We are after all keeping a history of the temperature readouts! A quick way to add history viewing to your application is to add the 'HistoryViewer.ocx' ActiveX control to your appliation. Clicking on the icon during run-time will launch the Local History Viewer (which if configured will target the appropriate server). However, let's not do this, as it is not very instructive at this stage. The local history viewer is after all another independent application, and we'd like to include a trend of the temperature inside our application. So add another Acop control to your application and call it for instance sineTemp. Your form should now look like:

Now right click on the new acop to get its property pages, check the 'Query EGU' check box and and browse your way to your server and the 'Temperature Property' then click on the 'apply' button. Now select the 'Graph' tab on the Property Pages and change the Graph type to 'TIME_LIN' and then 'OK' to close the property pages. We aren't going to use this Acop control for data acquistion, but we made use of the 'Query EGU' button to set the y-axis label and the y maximum and minimum settings. And we're going to use it as a trend chart, were Acop already knows how to deal with UTC timestamps if we're plotting 'TIME' on the x-axis.

Not add a global variable to hold the display handle off the new acop. Call it idTempHist for instance:

Dim idTempHst As Long


And add the following new lines of code in your combo box's click event:

Dim rc As Integer
Dim tnow As Long
Dim hstTemperature() As Single
Dim hstTimes() As Long
Dim npts As Integer
tnow = UNIXTIME - 1
SineTemp.xmax = tnow
SineTemp.xmin = tnow - 3600
rc = GetHistoryData("/WORKSHOP/WKSineGen/" + Combo1.text, "Temperature", 0, tnow - 3600, tnow, hstTemperature(), hstTimes(), npts)
SineTemp.ClearScreen
idTempHst = SineTemp.Draw(hstTemperature, hstTimes, , , npts, , 3600)


So now, every time we select a new device, in addition to starting all the links to get the relevant live data, we acquire the last hour's worth of temperature data and display it in our trend chart. If we stopped here, the history display wouldn't update at all, and the display would not be very useful. To make it update properly, add the following line inside the primary acop's (sineCurve) receive event:

If idTempHst >= 0 Then
SineTemp.AppendScreen s.temperature, idTempHst, 1, , SineCurve.timestamp
End If


Just to review, the receive event should now look something like:

Private Sub SineCurve_Receive(ByVal LinkIndex As Long, ByVal StatusCode As Long)
SineCurve.ClearScreen
SineCurve.ClearText
If StatusCode Then
SineCurve.PrintText SineCurve.Status
End If
SineCurve.Draw s.trace
Text1.text = str(s.temperature)
Text2.text = str(s.amplitude)
If idTempHst >= 0 Then
SineTemp.AppendScreen s.temperature, idTempHst, 1, , SineCurve.timestamp
End If
End Sub


Multicasting Data

There's one other thing you can easily try out before we leave this exercise, and that's multicast. Every tine server is capable of sending its data payload per multicast instead of peer-to-peer unicast. This has a tremendous advantage when there are large payloads which are needed by multiple clients. This is not a good idea in general, since if there are lots of multicasts on the net a client will then have to sift through all of them to find what it's looking for. In tine release 4.0, this scheme will be improved when the available set of multicast groups will encompose all servers on the control network, and a client will joing only those groups it's interested in. But for now we'll try to avoid multicasting unless there are more than 4 clients interested in a payload of 64 Kbytes or larger.

But just for fun, let's assume we have that case and our sine curve program has become very popular. All you have to do is go into the Combo Box's click event and change the accessMode from "POLL" to "POLL.NETWORK". That's it. You can try this out, and the behavior of your client program will not change in any way. However your server is now sending the requested data as a multicast. Start your client program on as many machines as you like and your server will process and send the payload only once. To see this, start the FEC Remote Control panel, select the context "WORKSHOP" and find your FEC (you may have to check the 'test' check box). Look at the 'Clients' Tab and you'll your server has you as a client (that's the Fec Remote Control Panel itself) as well as 'NETWORK' as a client (that's the multicasted data). Check the 'Contracts' tab and you'll see how many clients the contracts to 'Sine', 'Temperature' and 'Amplitude' have. There should be only one. If you start your client program a second time somewhere else, there will still be only one.


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