- Note
- Most .NET applications (be they VB.NET or C#) can run on Linux and other platforms under 'mono'. You would need to simply copy the .NET assembly to e.g. your linux machine and run 'mono myapplication.exe' in analogy with running 'java myapplication.jar'.
Setting up a GUI application
- Start Visual Studio and create a new Visual Basic Project in the workspace.
You might have to select 'Other Languages' if C++ or C# is set as your 'default platform'.
- Choose a Visual Basic Windows application and then a Windows Forms Application (you could also write a console application in Visual Basic, but we're interested in writing a GUI at this stage).
- Select a meaningful application name if you don't like the proposed 'WindowsApplication1' suggestion from Visual Studio. You can call it something like "vbClient", if you'd like.
- Hit the OK button.
You're probably now in 'Design Mode'.
- Find the 'Toolbox' on the far right hand edge and click on. You will see the available design widgets. I would suggest 'pinning' the Toolbox so that it stays visible.
- Drag a button and a label to the design form (both found in 'common controls').
- Drag a chart on the form as well. This is found under the 'Data' controls. We don't have an 'acop.NET' at the time of this tutorial (one is coming soon!), so we will have to make do with the Microsoft Chart tool and acquire our data directly via the TINE API.
- The above controls have been given default names such as 'Button1', 'Label1', and 'Chart1'. You can change these if you like. In fact you can change any properties of a .NET control at design time. Just right click on the control and select 'Properties' to see the properties side bar.
- The Chart control is 'powerful', has a completely different interface from ACOP and takes some getting used to. A Chart can have many 'series' or display items (curves). By default it has one (called by default 'Series1'). This is fine for us now.
- Under the 'Chart' properties click on 'Series' and a 'Series Collection Editor' will pop up. Now you can change various properties of this one display series.
- Let's change the default 'Column' representation (analogous to a 'historgram' type in ACOP) to a Line. Click on the Chart Type to see the enumerated combo items.
- You can change any other properties, such as names here in design mode. The names for the controls will appear in the generated code, so it's better to set them to what you would like in design mode. You're of course free to leave them as 'Button1', 'Label1', and 'Chart1'. Your design might now look like this:
- Now in double click on the button (or any of the controls, for that matter). The 'Form1.vb' code module will now appear for editing and, if you clicked on the button, it will have generated a 'Click' event routine called 'Button1_Click' as is waiting for you to write the code that should be executed when someone clicks on the button during run time.
- Before we write any code there, let's make the application 'TINE aware'.
- In the Solution Explorer, right click on the highlighted module (probably 'vbClient' if that was the name you chose for the application (or did you leave it as 'WindowsApplication1' ?).
- You should see a pulldown menu and you should select 'Add Reference'.
- On the upper right you can 'search browse' your way to 'L:\System32\tine.dll' and add this (make sure that the check box is checked).
- Now, before you write any code your button's click event. Add the line 'Imports tine' at the top of the Form1.vb module. And and two variables within the Form1 Class:
Dim myLnk As TLink
Dim buf(1023) As Single
- Now add the following inside your button's click event:
myLnk = New TLink("/WORKSHOP/Station1/Device 0", "Sine", New TDataType(buf), TDataType.empty, Access.CA_READ)
myLnk.Attach(New TLink.TLinkCallback(AddressOf lcb))
- We're going to create a TINE link object targeting our server and attach this link to a callback routine. Visual basic has probably underlined the callback in red, because it doesn't exist yet and Visual Basic cannot compile the code. Let's worry about this in a second.
- Let's now initialize the displayed data in the chart, which is defaulted to display about 8 items or so. Add the following in the Form1_Load routine.
For i = 0 To 1023
Chart1.Series(0).Points.AddY(buf(i))
Next
- Now back to the callback routine. Create an empty callback routine such as the following:
Public Sub lcb(lnk As TLink)
End Sub
Now the 'red' has disappeared from the Visual Basic editor. But this callback doesn't do anything. We would like to update our chart when new data arrive and maybe update the label text. The code we want is shown below
Public Sub lcb(lnk As TLink)
Chart1.Series(0).Points.Clear()
For i = 0 To 1023
Chart1.Series(0).Points.InsertY(0, buf(i))
Next
Chart1.Invalidate()
Label1.Text = Now.ToString
End Sub
This will run through our 'output' data buffer and 'insert' it into the y-axis in 'Series1' on our chart
(ACOP is a bit more transparent on how to draw incoming data here, but for the time being we'll have to stick with the chart). We first clear the chart, then insert the data, then we 'invalidate' the chart display, so that windows will redraw it.
We also update the contents of the label to show the current time of the update. Why?
It turns out that the callback is coming from a different thread than the one which created the graphics objects (in this case, the chart and the label) that the callback is trying to update. And this isn't allowed.
The way around this is to 'finesse' the callback into doing what we want by making a 'delegate' (this is along the lines of 'invoke later' in java).
Add a 'delegate' and insert the lines of code shown below so that the callback and delegate variable now look like:
Private Delegate Sub lcbDelegate(lnk As TLink)
Public Sub lcb(lnk As TLink)
If (InvokeRequired) Then
Invoke(New lcbDelegate(AddressOf lcb), lnk)
Exit Sub
End If
Chart1.Series(0).Points.Clear()
For i = 0 To 1023
Chart1.Series(0).Points.InsertY(0, buf(i))
Next
Chart1.Invalidate()
Label1.Text = Now.ToString
End Sub
This basically says that if the routine is called from another thread (and it will be) then pass the arguments along to a delegate that will be 'invoked' later and then return before actually doing anything. Otherwise it's okay to run through the routine.
All together your code so far should look something like:
Imports tine
Public Class Form1
Dim myLnk As TLink
Dim buf(1023) As Single
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For i = 0 To 1023
Chart1.Series(0).Points.AddY(buf(i))
Next
End Sub
Private Delegate Sub lcbDelegate(lnk As TLink)
Public Sub lcb(lnk As TLink)
If (InvokeRequired) Then
Invoke(New lcbDelegate(AddressOf lcb), lnk)
Exit Sub
End If
Chart1.Series(0).Points.Clear()
For i = 0 To 1023
Chart1.Series(0).Points.InsertY(0, buf(i))
Next
Chart1.Invalidate()
Label1.Text = Now.ToString
'myLnk.GetCompletionDataTimeStamp()
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
myLnk = New TLink("/TEST/WinSineServer/SineGen0", "Sine", New TDataType(buf), TDataType.empty, Access.CA_READ)
myLnk.Attach(New TLink.TLinkCallback(AddressOf lcb))
End Sub
End Class
- If you now run this, you should see an updating chart after you click the button. Of course, if your server isn't running, or you've made a typographical mistake in the address used in establishing the link it won't update.
- We have programed 'optimistically'. It's a good idea to check the link status of the link that is being passed in the callback! We have just assumed that everything is okay and that our data buffer 'buf' has been filled with new data prior to the callback.
- Exercises here include: check the link status and print the status in another label.
- You can also add other links to the application. e.g. get the gaussian display and display it.
- Either add another chart or add another series to the same chart.
TODO: finish this ...