Hi All:
I am writing a project to analyze data from a device in real time. The idea is to pick up the signal from a sensor through serial port in 20Hz and to do some calculation on the data received, and plot out the result on the form.
I use a background worker to read data from the serial port and do the calculation within a Do Loop. There is no problem on this. My problem is that when I try to use GDI+ to plot the results on the form, data are not refreshed instantly on the form. I use a Collection of Single to store the data for plotting.
Questions are:
1. Can Vb2005 do the job Or VC2005 is better
2. Is there a control I can use to plot instant data
3. Can I synchronize two threads I tried to let two background workers to run at the same time, but I noticed that only one is running, the other one is waiting.
Any suggestions would be very much appreciated.

Real Time Data Monitoring
Goodway
Don't worry about shifting at all. That's very memory and compute intensive.
Keep an index to the current data point. When you reach the array boundary reset the index to 0. This is what is meant by a circular buffer.
You don't have to shift anything instead you increment an integer index.
forummember
Hi SJWhiteley:
"a collection is probably not the best way to store the data "
Could you give a clue of the best way to store the data. I tried to use a Collection or a Quere to store the data, by adding an item in the beginning position and removing an item from the end to keep the number of items fixed. I searched the web. Someone uses Circular Buffer for this purpose. I am wondering how it is achieved.
Many thanks.
Michael Brooks
How fast are you refreshing the screen 100mS, 50ms, 20mS What speed do you want
VB will be fine, but you do have diminishing return. Depending on the quantity of data points, a collection is probably not the best way to store the data (it may be a cool mechanism, but more than likely, inappropriate). You won't much, if any, performance increase using C++ over VB using GDI+. Go ahead and try it: do some benchmarks for yourself.
Have a search on the net for activeX controls, or .NET controls that graph data: some free, the better ones you will have to buy. Or do it yourself.
Separate your data acquisition from display. A timer is typically the best way to perform data refresh on a regular basis: depending on the data rate, and what the user is expecting, a rate of 100mS update down to 30mS may be appropriate for an HMI type display.
(the form timer doesn't cut it, though. You'll have to use a multimedia timer, or a thread/system timer).
If you use your data acquisition thread to format and store the data in a format that is immidiately usable by the GDI graphical commands you will gain better performance (since the DAQ routine will be only manipulatng several dozen data points instead of the GDI routines manipulating several thousand - or 100's of thousands - of data points - which don't change - every refresh cycle).
zjs
Thanks.
The data is received in 20Hz and I want to display 3 minutes data on the plot, therefore, about 3600 data points to be refreshed every 50ms. I did not refresh the form each time, but use the code to clear and plot the data:
Dim bm As Image= New Bitmap(PictureBox.Width, PictureBox.Height) Dim gr As Graphics gr = Graphics.FromImage(bm) Dim Rect As New RectangleF gr.Clear(Color.White) For i = 0 To WorkQ.Count - 1 ' (3600 data poitns)x = x - unitX
y = (maxUnit - WorkQ.Item(i)) * unitY
Rect.X = x
Rect.Y = y
gr.FillEllipse(Brushes.Red, Rect)
Next iPictureBox.BackgroundImage = bm
Sebastien Donche
Drawline is one of the graphics routines from gdi+ that must be wrapped for .NET.
I've used that to do graphing of line two pixels wide. It made for great points.
If I remember correctly it took about 30 milliseconds to plot 650 points
with this code:
start = GetTickCount()
For i As Short = 0 To 649
a.PlotPoint(i, (Math.Sin(i / 57.3) * 100) + 100)
Next
Stoptime = GetTickCount - start
Here is the graphics class I was calling:
g = pb1.CreateGraphics()
a = New GraphClass(g)
End Sub
Private Sub cbGo_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cbGo.Click
Static dtNow As DateTime = DateTime.Now
a.SetupFrame()
start = GetTickCount()
For i As Short = 0 To 649
a.PlotPoint(i, (Math.Sin(i / 57.3) * 100) + 100)
Next
Stoptime = GetTickCount - start
End Sub
Private Sub cbExit_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cbExit.Click
End
End Sub
Public Class GraphClass
Protected Gain As New Single
Private Intersection As Point = New Point(FrameCoord.HorizontalDisplacement, FrameCoord.X_Axis_VertLoc)
Protected g As Graphics ' picture box graphic
Protected AxisColor As Color = System.Drawing.ColorTranslator.FromOle(&HFF00)
Protected AxisPen As Pen
Public Sub New(ByVal obj As Graphics)
' this graphics is derived from a picture box object in the substantiator
Gain = 1.0
g = obj
AxisPen = New Pen(AxisColor, 2)
End Sub
Public Sub SetupFrame()
Dim pY_Begin As New Point(FrameCoord.HorizontalDisplacement, FrameCoord.Y_Axis_Start)
Dim pY_End As New Point(FrameCoord.HorizontalDisplacement, FrameCoord.X_Axis_VertLoc)
Dim pX_Begin As Point = Intersection
Dim pX_End As New Point(FrameCoord.Y_Axis_XEnd, FrameCoord.X_Axis_VertLoc)
' Draw line to screen.
Drawline(AxisPen, pY_Begin, pY_End)
Drawline(AxisPen, pX_Begin, pX_End)
For i As Short = FrameCoord.HorizontalDisplacement + 50 To FrameCoord.Y_Axis_XEnd Step 50
Drawline(Pens.BlanchedAlmond, New Point(i, FrameCoord.X_Axis_VertLoc - 10), New Point(i, FrameCoord.X_Axis_VertLoc))
Next
For i As Short = FrameCoord.X_Axis_VertLoc - 50 To FrameCoord.Y_Axis_Start Step -50
Drawline(Pens.BlanchedAlmond, New Point(FrameCoord.HorizontalDisplacement, i), New Point(FrameCoord.HorizontalDisplacement + 10, i))
Next
End Sub
Public Sub PlotPoint(ByVal X As Integer, ByVal Y As Integer, Optional ByVal pn As Pen = Nothing)
Dim xp As Integer = (-Y + FrameCoord.X_Axis_VertLoc) * Gain
If pn Is Nothing Then
g.DrawLine(AxisPen, New Point(X + Intersection.X, xp), New Point(X + 1 + Intersection.X, xp + 1))
Else
g.DrawLine(pn, New Point(X + Intersection.X, xp), New Point(X + 1 + Intersection.X, xp + 1))
End If
End Sub
Public Sub Drawline(ByVal P As Pen, ByVal StartPoint As Point, ByVal EndPoint As Point)
If P Is Nothing Then
g.DrawLine(AxisPen, StartPoint, EndPoint)
Else
g.DrawLine(P, StartPoint, EndPoint)
End If
End Sub
End Class
Keep in mid that the code is also computing a sine at the same time.
I even remembered that there are 57.3 degrees in a radian!
I almost forget to mention that my machine is a 3.2 ghz hyperthreaded machine.
PLCweaver
Thank you handsome....
You just taugh me something....... It never occurred to me to store an array of points.
That's a really groovy idea.
Mieux
Hi Jiao,
I can probably answer part of your questions.
1. If you are using GDI+ in the managed code, then VC2005 and VB2005 should do the same job.
2. Not sure.
3. Should be able to but I am not very familiar with that.
By the way, have you tried to look at Form.invalidate and Form.Update method to refresh the drawing.
John
Fagutish
Hi ReneeC:
If an array is used, I have to shift the whole array items one by one when new data is added:
dim data(100) as array
for i=100 to 1 step -1
data(i)=data(i-1)
next
data(0)=0.16 'new coming data
If I use a Collection, all data items are shifted automatically if an item is inserted at 0 position. I just need to remove the item at position 100. Am I right
George Bolsch
Hi cgraus:
I am new on GDI+, could you advise the proper way to plot the points.
Many thanks.
Il-Sung
SetPixel is the method you want to just plot a single point.
licheca
What Renee said
.
Additionally, you could redimension the array if your point count got too big, or you wanted to increase the storage capacity dynamically (most of the applications I've worked/work on I don't know how many points I'll need, so I use this method).
Also, for refreshing, you won't need to 'plot' the whole array: just the points that are added. Of course, if the application is minimized, resized, ec. you'll need to redraw everything.
You are correct about the collection: it does save time adding points to it, however, that isn't an 'expensive' action. Retrieving and plotting needs to be as fast as possible. In that case, I would probably store an array of Points, rather than an array of single values, since those singles will need converting to points each and every time you need to plot (if you are plotting points, that is). Also, since memory is cheap, if you may need to readjust the point values (rescale, etc.) you may want to store two arrays: an array of the real world values and an array of points.
Basically, you won't get something for nothing: the simplicity of the collection if offset by a time consuming retrieval and plotting. You can gain speed at the expense of memory - but you have to keep a check on it and know that you are consuming those resouces like a glutton, and will eventually run out, or will hinder performance.
Sedigh
An array
prasannadotnet
Does this draw more than a dot If it's drawing a dot, surely FillEllipse is a point of considerable cost
Herwin Grauel
Public Class Form1
Protected Const Bufsize As Short = 1000
Protected Buf(Bufsize-1) As Single
Protected Index As Short
Private Sub StoreDataPoint(ByVal InVal As Single, Optional ByVal Start As Boolean = False)
If (Start Or (Index > Bufsize-1)) Then Index = 0
Buf(Index) = InVal
Index += 1
End Sub
StoreDataPoint manages the data acquisition for you. Index and the buffer is acessible to everyone, when you are not acquiring or processing it, other routines have access to it to. The nice thing about this is that there no shifting and data storage is only a few machine instructions. Setting Start resets the buffer to zero. Index always tells you where the usable data is. It's behind Index in the buffer. It is a good idea to use Start though.