-
Sep 3rd, 2020, 07:15 AM
#1
Thread Starter
Member
[RESOLVED] How to Handle Serial data event and new frame event using Aforge library at same time
hello everyone,
I'm using AFORGE video library in my project for video capturing. Also, I'm using serial data receive event for capturing incoming serial data.
My issue is that when I run both together then my project hangs and video capture get's very slow.
Do I need to use Video capture in another thread ? The video new frame event fires whenever there is a new frame from the camera and record the video.
below is my code regarding the problem.
Code:
Imports AForge.Video
Imports AForge.Video.DirectShow
Imports Accord.Video.FFMPEG
Dim CAMERA As VideoCaptureDevice
Dim BMP As Bitmap
Dim VIDEOFRAME As Bitmap
Dim RECORDER As New VideoFileWriter()
Dim LOADER As New VideoFileReader()
Dim FPS As Integer = 30
Dim BRT As Integer = 300
Private Sub SerialPort1_DataReceived(sender As Object, e As IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
While SerialPort1.BytesToRead > 0
datastring = SerialPort1.ReadLine
datapacket = Integer.Parse(datastring)
Me.BeginInvoke((New MyDelegate(AddressOf DrawGraph2)), datapacket)
End While
End Sub
Private Sub DrawGraph2(XVALUE1 As Integer)
//plot data
End Sub
Private Sub Video_ToolStripButton_Click(sender As Object, e As EventArgs) Handles Video_ToolStripButton.Click
If Video_ToolStripButton.Text = "Video Off" Then
Dim CAMARAS As VideoCaptureDeviceForm = New VideoCaptureDeviceForm()
If CAMARAS.ShowDialog() = DialogResult.OK Then
CAMERA = CAMARAS.VideoDevice
AddHandler CAMERA.NewFrame, New NewFrameEventHandler(AddressOf CAPTURER)
CAMERA.Start()
End If
Video_PictureBox.Location = New Point(Panel1.Width - (Video_PictureBox.Width + 10), 5)
Video_PictureBox.Visible = True
Video_ToolStripButton.Text = "Video On"
ElseIf Video_ToolStripButton.Text = "Video On" Then
Video_ToolStripButton.Text = "Video Off"
Video_PictureBox.Visible = False
End If
End Sub
Private Sub CAPTURER(sender As Object, eventArgs As NewFrameEventArgs)
If Video_ToolStripButton.BackColor = Color.White Then
BMP = DirectCast(eventArgs.Frame.Clone(), Bitmap)
Video_PictureBox.Image = DirectCast(eventArgs.Frame.Clone(), Bitmap)
Else
Try
BMP = DirectCast(eventArgs.Frame.Clone(), Bitmap)
Video_PictureBox.Image = DirectCast(eventArgs.Frame.Clone(), Bitmap)
RECORDER.WriteVideoFrame(BMP)
Catch ex As Exception
End Try
End If
End Sub
kindly help in resolving the issue.
-
Sep 3rd, 2020, 07:40 AM
#2
Re: How to Handle Serial data event and new frame event using Aforge library at same
As far as the serial port goes you have it hung up waiting for data. In general it is bad practice to have the DataReceived event handler waiting. In the example below I show an alternative method. You should be able to modify the code after this line, 'process your message here <<<<<<<<<<<<, to handle your situation. Hopefully it helps.
Code:
Private ReadSerialPortTask As task
Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
'start the worker task <<<<<<<<<<<<!!!!!
'this could be in the load event also
ReadSerialPortTask = Task.Run(Sub() ReadSerialPort())
End Sub
Private Sub SerialPort1_DataReceived(sender As Object,
e As IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
'this is it
ReadWait.Set() 'cause ReadSerialPort to run
End Sub
Private ReadWait As New Threading.AutoResetEvent(False)
Private Sub ReadSerialPort()
'
'this is run as a Background task
'
Dim data As New System.Text.StringBuilder(1024 * 1024) 'accumulate SerialPort data here
Dim mess As New System.Text.StringBuilder 'individual message
Dim foundMess As Boolean = False
Do
ReadWait.WaitOne() 'wait for SerialPort1.DataReceived
Do While SerialPort1.BytesToRead > 0 'accumulate all available bytes
data.Append(SerialPort1.ReadExisting)
Loop
Do 'process message, if one available
mess.Length = 0 'reset message
foundMess = False 'not found
Dim idx As Integer = 0
For idx = 0 To data.Length - 1 'look for CR
If data(idx) = ControlChars.Cr Then
'note that the CR will NOT be in mess
foundMess = True 'found message
Exit For
End If
mess.Append(data(idx))
Next
If foundMess Then
data.Remove(0, idx + 1) 'remove found message from data
'
'process your message here <<<<<<<<<<<<
'note how UI interaction is handled in example
'
'example MY message has two parts separated by tab
Dim parts() As String
parts = mess.ToString.Split(ControlChars.Tab)
'how to interact with the UI
Me.Invoke(Sub()
Label1.Text = parts(0)
RichTextBox1.AppendText(parts(1))
RichTextBox1.AppendText(ControlChars.Cr)
RichTextBox1.ScrollToCaret()
End Sub)
'end example
End If
'look for more messages in data?
Loop While foundMess AndAlso data.Length > 0
Loop
End Sub
-
Sep 4th, 2020, 03:08 AM
#3
Thread Starter
Member
Re: How to Handle Serial data event and new frame event using Aforge library at same
Thanks for your valuable reply.
But I am not able to use Task because I'm using Dotnet framework 3.5 (Aforge Video Library works only in Dotnet Framework 3.5 and below).
So, Please provide me alternative for this. I shall be very thankful .
-
Sep 4th, 2020, 05:52 AM
#4
Thread Starter
Member
Re: How to Handle Serial data event and new frame event using Aforge library at same
Hi,
I used TASKPARALLELLIBRARY which is meant for .net framework 3.5. I followed your suggestion and made necessary code changes. below is the code I tried. Im reading data as readline because my incoming data is terminating with a newine.
Code:
Private ReadSerialPortTask As task
Private ReadWait As New Threading.AutoResetEvent(False)
Private Sub Acquire_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim monitorWidth As Integer = My.Computer.Screen.WorkingArea.Size.Width
Dim monitorHeight As Integer = My.Computer.Screen.WorkingArea.Size.Height
' --- adjust size of this form ** this does NOT make consideration of DPI (eg. 125%, 150%) **
Me.Width = monitorWidth
Me.Height = monitorHeight
Control.CheckForIllegalCrossThreadCalls = False
Plotting_Signal = New Bitmap(Panel1.Width, Panel1.Height)
Dim g As Graphics = Me.CreateGraphics
Monitor_PPI_On_Y = g.DpiY
Pixels_Per_MM = Monitor_PPI_On_Y / 25.4
Sens_factor = SensFunction(7.5)
SweepFunction(10)
SerialPort1.PortName = "COM7"
ReadSerialPortTask = Task.Factory.StartNew(Sub() ReadSerialPort())
End Sub
Private Sub ReadSerialPort()
'this is run as a Background task
Do
ReadWait.WaitOne() 'wait for SerialPort1.DataReceived
Do While SerialPort1.BytesToRead > 0 'accumulate all available bytes
datastring = SerialPort1.ReadLine
datapacket = Integer.Parse(datastring)
Me.BeginInvoke((New MyDelegate(AddressOf DrawGraph2)), datapacket)
Loop
Loop
End Sub
I can still see some hung up in UI response. for example if I click a button , The button event fires a little late after hanging up.
Iam even recording the video and storing serial data in a binary file. The recorded video & stored data both are OK. It's the UI which has slow response.
I have started the TASK in Form Load event instead of FORM shown. Does it make any difference?
looking forward for your reply.
-
Sep 4th, 2020, 07:26 AM
#5
Re: How to Handle Serial data event and new frame event using Aforge library at same
My opinion is that you should do this,
Code:
Private ReadSerialPortTask As Task
'Private ReadSerialPortThread As Threading.Thread
Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
'start the worker task <<<<<<<<<<<<!!!!!
'this could be in the load event also
ReadSerialPortTask = Task.Factory.StartNew(Sub() ReadSerialPort())
'ReadSerialPortThread = New Threading.Thread(AddressOf ReadSerialPort)
'ReadSerialPortThread.IsBackground = True
'ReadSerialPortThread.Start()
End Sub
Private Sub SerialPort1_DataReceived(sender As Object,
e As IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
'this is it
ReadWait.Set() 'cause ReadSerialPort to run
End Sub
Private ReadWait As New Threading.AutoResetEvent(False)
Private Sub ReadSerialPort()
'
'this is run as a Background task
'
Dim data As New System.Text.StringBuilder(1024 * 1024) 'accumulate SerialPort data here
Dim mess As New System.Text.StringBuilder 'individual message
Dim foundMess As Boolean = False
Do
ReadWait.WaitOne() 'wait for SerialPort1.DataReceived
Do While SerialPort1.BytesToRead > 0 'accumulate all available bytes
data.Append(SerialPort1.ReadExisting)
Loop
Do 'process message, if one available
mess.Length = 0 'reset message
foundMess = False 'not found
Dim idx As Integer = 0
For idx = 0 To data.Length - 1 'look for CR??????
If data(idx) = ControlChars.Cr Then '<<<<<<<<<<<<<<<<<<<<<<<
'note that the CR will NOT be in mess
foundMess = True 'found message
Exit For
End If
mess.Append(data(idx))
Next
If foundMess Then
data.Remove(0, idx + 1) 'remove found message from data
'
'process your message here <<<<<<<<<<<<
'
Dim datapacket As Integer
If Integer.TryParse(mess.ToString, datapacket) Then
Me.Invoke((New MyDelegate(AddressOf DrawGraph2)), datapacket)
Else
'invalid
End If
End If
'look for more messages in data?
Loop While foundMess AndAlso data.Length > 0
Loop
End Sub
So I tried to help and you decided not to take my advice.
The problem with the UI might be something else entirely. If you had read the comments one of the questions was answered.
-
Sep 4th, 2020, 09:48 AM
#6
Re: How to Handle Serial data event and new frame event using Aforge library at same
I tend to use the DataReceived event and collecting data and frame parsing, etc... when the data to be read can't be self framing, i.e. you can't use ReadLine.
If you can use ReadLine, so the messages are self framing, then I use that. I don't have any loop in the event, and I don't use ReadExisting.
You get an event, you do the ReadLine, you process the line and exit the event. Very straight forward.
If your processing of the data to do the graphing is hanging up your GUI thread, then you may need to consider not invoking back to the GUI thread to do the bulk of the drawing, or any of the drawing.
Either do the drawing in a bitmap from the background thread, and then only invoke the Gui thread to update the panel using the bitmap, or take it a step further and create a Buffered Graphics Object and use the panel as it's display surface. That way you can do all the processing and drawing in the background thread using the Buffered Graphics Object's graphic object and refresh the image area of the panel from the background thread. You wouldn't be doing any drawing or updating of the panel from the GUI thread, so no cross-threading issue.
An example of using the BufferedGraphics object to update the image area of a panel from a background thread.
"Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930
-
Sep 4th, 2020, 10:42 AM
#7
Re: How to Handle Serial data event and new frame event using Aforge library at same
Originally Posted by passel
I tend to use the DataReceived event and collecting data and frame parsing, etc... when the data to be read can't be self framing, i.e. you can't use ReadLine.
If you can use ReadLine, so the messages are self framing, then I use that. I don't have any loop in the event, and I don't use ReadExisting.
You get an event, you do the ReadLine, you process the line and exit the event. Very straight forward....
The problem with a self framing message using readline in the DataReceived event handler is that that handler might fire multiple times before a line termination character is received.
-
Sep 4th, 2020, 11:04 AM
#8
Re: How to Handle Serial data event and new frame event using Aforge library at same
Doesn't matter. Each event is raised in a different thread in that case. You wouldn't loose anything since you're reading the buffer from the first event.
I haven't examined in detail whether you end up with a number of parallel threads with pending ReadLines in them. It seemed to me that you might end up with two, one that is processing the ReadLine, and possibly a second to raise an event for data being received that isn't being handled by the current ReadLine.
If a Readline is active, I don't think a data received event is generated for new data that is being drained by the Readline. I believe the serial driver is filling the Readline request, and won't raise another event unless it has new data after satisfying the Readline.
If I only have data that can be processed by Readline, so don't need to switch between Readline and Readexisting, I usually don't even bother with coding an event handler for the DataReceived event.
I just start a background thread, and have a loop, and let the background thread sit on the Readline call. The serial driver will buffer the data, and return the string to the call on that background thread, I'll process it, and then loopback and suspend on the Readline until the Serial driver returns the next line.
That is the whole reason Readline exists, to make it easy to process serial data that is framed by lines, letting the thread suspend on the call, and the serial driver buffer the data and return the line to the suspended thread when it has a line. No need for a DataReceived event handler in that scenario.
"Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930
-
Sep 5th, 2020, 12:30 AM
#9
Thread Starter
Member
Re: How to Handle Serial data event and new frame event using Aforge library at same
Hi,
I tried implementing your suggested code. The UI is running fine now, But due to read existing the data is incomplete at the end of array in which we are appending the serial buffer.
The incoming serial data is terminating with "\n" But in Read existing mode, It is not necessary that the last byte will be of new line character only. It will simply just dump whatever is in the serial buffer without taking the newline ending criteria.
-
Sep 5th, 2020, 02:20 AM
#10
Lively Member
Re: How to Handle Serial data event and new frame event using Aforge library at same
If I only have data that can be processed by Readline, so don't need to switch between Readline and Readexisting, I usually don't even bother with coding an event handler for the DataReceived event.
I just start a background thread, and have a loop, and let the background thread sit on the Readline call. The serial driver will buffer the data, and return the string to the call on that background thread, I'll process it, and then loopback and suspend on the Readline until the Serial driver returns the next line.
That is the whole reason Readline exists, to make it easy to process serial data that is framed by lines, letting the thread suspend on the call, and the serial driver buffer the data and return the line to the suspended thread when it has a line. No need for a DataReceived event handler in that scenario.
Can You share an example of this?
-
Sep 5th, 2020, 07:29 AM
#11
Re: How to Handle Serial data event and new frame event using Aforge library at same
Originally Posted by Vinod Chauhan
Hi,
I tried implementing your suggested code. The UI is running fine now, But due to read existing the data is incomplete at the end of array in which we are appending the serial buffer.
The incoming serial data is terminating with "\n" But in Read existing mode, It is not necessary that the last byte will be of new line character only. It will simply just dump whatever is in the serial buffer without taking the newline ending criteria.
Is the termination character CR or LF? How do you know when you have a complete message?
-
Sep 6th, 2020, 02:33 AM
#12
Thread Starter
Member
Re: How to Handle Serial data event and new frame event using Aforge library at same
Hi,
I checked my incoming string data. It is like "82763"&vbCrLf&"82507"&vbCrLf &"82769"&vbCrLf.
The number is the actual data followed by CrLf.
I tried using If data(idx) = ControlChars.CrLf or ControlChars.Newline ,but it never gets true. It only goes inside If either by checking ControlChars.Cr or ControlChars.Lf
-
Sep 6th, 2020, 06:27 AM
#13
Re: How to Handle Serial data event and new frame event using Aforge library at same
Ignore one terminator
Code:
If data(idx) = ControlChars.Cr Then '<<<<<<<<<<<<<<<<<<<<<<<
'note that the CR will NOT be in mess
foundMess = True 'found message
Exit For
ElseIf data(idx) = ControlChars.LF Then '<<<<<<<<<<<<<<<<<<<<<<<
Continue For
End If
-
Sep 8th, 2020, 07:52 AM
#14
Thread Starter
Member
Re: How to Handle Serial data event and new frame event using Aforge library at same
when I used Me.invoke the data doesn't plotted properly..
but While using Me.BeginInvoke Plotting is ok..
Please tell me the difference Between Me.Invoke And Me.BeginInvoke.
-
Sep 8th, 2020, 08:38 AM
#15
Re: How to Handle Serial data event and new frame event using Aforge library at same
-
Sep 8th, 2020, 07:45 PM
#16
Junior Member
Re: How to Handle Serial data event and new frame event using Aforge library at same
-
Sep 9th, 2020, 08:06 AM
#17
Thread Starter
Member
Re: How to Handle Serial data event and new frame event using Aforge library at same
me.invoke means it works synchronously on same thread & Me.Begininvoke means it works asynchornously on a threadpool thread.
When I use Me. Invoke, my data is passed to Main UI thread and gets processed . However I still do not understand why my data gets corrupted in between when I use Me. Invoke but my UI is running fine & responsive.
When I use Me.BeginInvoke, My data is correct but UI starts hanging up.
Iam still not able to understand the difference between the two methods.
How can I use Me. Invoke correctly?
-
Sep 9th, 2020, 08:25 AM
#18
Re: How to Handle Serial data event and new frame event using Aforge library at same
-
Sep 9th, 2020, 10:11 AM
#19
Re: How to Handle Serial data event and new frame event using Aforge library at same
Originally Posted by Vinod Chauhan
me.invoke means it works synchronously on same thread & Me.Begininvoke means it works asynchornously on a threadpool thread.
When I use Me. Invoke, my data is passed to Main UI thread and gets processed . However I still do not understand why my data gets corrupted in between when I use Me. Invoke but my UI is running fine & responsive.
When I use Me.BeginInvoke, My data is correct but UI starts hanging up.
Iam still not able to understand the difference between the two methods.
How can I use Me. Invoke correctly?
Most likely, when you use Invoke, then you are suspending the thread the ReceivedData event used for the event, and the Gui Thread is released to process events, in particular the Invocation that you did from that suspended thread. So, while you're doing the drawing in the GUI thread, most likely there are other events pending so you process those, and one of those pending events can be another ReceivedData event on another thread. The Serial driver won't use the same background thread for the ReceivedData event if you have that thread tied up, so it generates a ReceivedData event on another thread. So, you may be processing another ReceivedData event while not have actually finished the the first thread, i.e. you haven't returned from the invocation yet because other events are pending.
It appears your drawing takes long enough that you probably have a number of background threads pending from backed up ReceivedData events to be processed, and you may be processing those out of order, so messing up your data, or it is possible that the threads get backed up enough so that you end up loosing Serial data because you overflow the serial buffer.
When you use BeginInvoke, the invocation is queued and will be processed the next time the GUI thread is allowed to run to process the event caused by the invocation. Thus you don't loose serial data, because you return immediately after queuing the event and exit the background thread you got the ReceivedData event in. The Serial data doesn't get backed up so you don't loose serial data, but you end up with a lot of invocations building up for the GUI thread to process, and thus your GUI processing gets quite slow.
Redoing your drawing for each point received is not a very efficient way to handle drawing anyway.
What I generally do is decide what speed I want to update the GUI and have a timer fire at that rate, say 10hz.
I collect data continually, and at the tick of the "Gui Clock" I plot whatever data I currently have collected. If I'm receiving 100 data points per second, I'm not updating the plot 100 times per second, but adding 10 points to the plot each time the clock ticks, so do 10 times less drawing, which helps keep the GUI free to process regular events.
Likewise, as I said, if there is a case where I may have many thousands of things to plot, then I will end up using a background thread to do the plotting instead, i.e. write to a bitmap and only invoke once from that background thread to update the plot from that bitmap. That way the drawing process itself won't impact the GUI thread or the serial threads.
You can use a concurrent queue to transfer data from the received data thread to the Gui (or other) thread, and have that thread read from the concurrent queue. Use of a concurrent queue takes care of the handshaking needed when two threads are accessing the same object, so that they don't conflict, but it is still best to minimize the time accessing the concurrent queue from either thread so you don't hangup the access to the other thread for long.
Last edited by passel; Sep 9th, 2020 at 11:04 AM.
"Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930
-
Sep 10th, 2020, 12:21 AM
#20
Thread Starter
Member
Re: How to Handle Serial data event and new frame event using Aforge library at same
Hi,
Iam atatching the pictures of GUI for your reference to show the issue.
Data with begininvoke
Attachment 178661
data with invoke method
Attachment 178662
below is the code
Code:
Public Delegate Sub MyDelegate(ByVal indata1 As Integer)
Private ReadSerialPortTask As task
Public mygraphics As Graphics
Public p2 As New Pen(Color.Black, 0.2)
Private Plotting_Signal As New Bitmap(100, 100)
Private ReadWait As New Threading.AutoResetEvent(False)
Private Sub SerialPort1_DataReceived(sender As Object, e As IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
ReadWait.Set() 'cause ReadSerialPort to run
End Sub
Private Sub ReadSerialPort()
'
'this is run as a Background task
'
Dim data As New System.Text.StringBuilder(1024 * 1024) 'accumulate SerialPort data here
' Dim data1(1024 * 1024) As String
' Dim data2(1024 * 1024) As String
Dim mess As New System.Text.StringBuilder 'individual message
Dim foundMess As Boolean = False
Dim er As Integer
Do
ReadWait.WaitOne() 'wait for SerialPort1.DataReceived
Do While SerialPort1.BytesToRead > 0 'accumulate all available bytes
data.Append(SerialPort1.ReadExisting)
'datastring = SerialPort1.ReadLine
''data1(er) = datastring
''er = er + 1
'datapacket = Integer.Parse(datastring)
'Me.BeginInvoke((New MyDelegate(AddressOf DrawGraph2)), datapacket)
Loop
Do 'process message, if one available
mess.Length = 0 'reset message
foundMess = False 'not found
Dim idx As Integer = 0
For idx = 0 To data.Length - 1 'look for CR??????
'data1(er) = data(idx)
If data(idx) = Constants.vbLf Then '<<<<<<<<<<<<<<<<<<<<<<<
' 'note that the CR will NOT be in mess
foundMess = True 'found message
Exit For
' 'ElseIf data(idx) = ControlChars.Lf Then
' ' Continue For
End If
mess.Append(data(idx))
Next
If foundMess Then
data.Remove(0, idx + 1) 'remove found message from data
'
'process your message here <<<<<<<<<<<<
'
Dim datapacket As Integer
'Dim datapacket1 As String
'datapacket1 = mess.ToString
'data2(er) = datapacket1
'er = er + 1
' datapacket = Integer.Parse(mess.ToString)
If Integer.TryParse(mess.ToString, datapacket) Then
Me.Invoke((New MyDelegate(AddressOf DrawGraph2)), datapacket)
'Else
' invalid
End If
End If
'look for more messages in data?
Loop While foundMess AndAlso data.Length > 0
Loop
End Sub
Private Sub DrawGraph2(XVALUE1 As Integer)
// all data processing code
Plot_Signal = True
Plot_Signal_Picture() // create bitmap picture
mygraphics = Plotting_Panel.CreateGraphics // panel on which drawing takes place
mygraphics.DrawImage(Plotting_Signal, 0, 0)
End Sub
Function Plot_Signal_Picture()
Plotting_Signal.Dispose()
Plotting_Signal = New Bitmap(Plotting_Panel.Width, Plotting_Panel.Height)
If Plot_Signal = True Then
mygraphics = Graphics.FromImage(Plotting_Signal)
mygraphics.PageUnit = GraphicsUnit.Pixel
mygraphics.PageScale = 1
mygraphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
mygraphics.ScaleTransform(1, 1)
Case 16
// this is case 16 to plot 16 different lines of data as shown in the picture attached.
For n = 0 To gMAX_SAMPLES - 1
Ch1_y2 = Intfilterbuffer(n, 0)
If Ch1_y2 > (Deflection * Pixels_Per_MM) Then
Ch1_y2 = Deflection * Pixels_Per_MM
ElseIf Ch1_y2 < (-1 * (Deflection * Pixels_Per_MM)) Then
Ch1_y2 = (Deflection * Pixels_Per_MM) * -1
End If
Ch2_y2 = Intfilterbuffer(n, 1)
If Ch2_y2 > (Deflection * Pixels_Per_MM) Then
Ch2_y2 = Deflection * Pixels_Per_MM
ElseIf Ch2_y2 < (-1 * (Deflection * Pixels_Per_MM)) Then
Ch2_y2 = (Deflection * Pixels_Per_MM) * -1
End If
Ch3_y2 = Intfilterbuffer(n, 2)
If Ch3_y2 > (Deflection * Pixels_Per_MM) Then
Ch3_y2 = Deflection * Pixels_Per_MM
ElseIf Ch3_y2 < (-1 * (Deflection * Pixels_Per_MM)) Then
Ch3_y2 = (Deflection * Pixels_Per_MM) * -1
End If
Ch4_y2 = Intfilterbuffer(n, 3)
If Ch4_y2 > (Deflection * Pixels_Per_MM) Then
Ch4_y2 = Deflection * Pixels_Per_MM
ElseIf Ch4_y2 < (-1 * (Deflection * Pixels_Per_MM)) Then
Ch4_y2 = (Deflection * Pixels_Per_MM) * -1
End If
Ch5_y2 = Intfilterbuffer(n, 4)
If Ch5_y2 > (Deflection * Pixels_Per_MM) Then
Ch5_y2 = Deflection * Pixels_Per_MM
ElseIf Ch5_y2 < (-1 * (Deflection * Pixels_Per_MM)) Then
Ch5_y2 = (Deflection * Pixels_Per_MM) * -1
End If
Ch6_y2 = Intfilterbuffer(n, 5)
If Ch6_y2 > (Deflection * Pixels_Per_MM) Then
Ch6_y2 = Deflection * Pixels_Per_MM
ElseIf Ch6_y2 < (-1 * (Deflection * Pixels_Per_MM)) Then
Ch6_y2 = (Deflection * Pixels_Per_MM) * -1
End If
Ch7_y2 = Intfilterbuffer(n, 6)
If Ch7_y2 > (Deflection * Pixels_Per_MM) Then
Ch7_y2 = Deflection * Pixels_Per_MM
ElseIf Ch7_y2 < (-1 * (Deflection * Pixels_Per_MM)) Then
Ch7_y2 = (Deflection * Pixels_Per_MM) * -1
End If
Ch8_y2 = Intfilterbuffer(n, 7)
If Ch8_y2 > (Deflection * Pixels_Per_MM) Then
Ch8_y2 = Deflection * Pixels_Per_MM
ElseIf Ch8_y2 < (-1 * (Deflection * Pixels_Per_MM)) Then
Ch8_y2 = (Deflection * Pixels_Per_MM) * -1
End If
Ch9_y2 = Intfilterbuffer(n, 8)
If Ch9_y2 > (Deflection * Pixels_Per_MM) Then
Ch9_y2 = Deflection * Pixels_Per_MM
ElseIf Ch9_y2 < (-1 * (Deflection * Pixels_Per_MM)) Then
Ch9_y2 = (Deflection * Pixels_Per_MM) * -1
End If
Ch10_y2 = Intfilterbuffer(n, 9)
If Ch10_y2 > (Deflection * Pixels_Per_MM) Then
Ch10_y2 = Deflection * Pixels_Per_MM
ElseIf Ch10_y2 < (-1 * (Deflection * Pixels_Per_MM)) Then
Ch10_y2 = (Deflection * Pixels_Per_MM) * -1
End If
Ch11_y2 = Intfilterbuffer(n, 10)
If Ch11_y2 > (Deflection * Pixels_Per_MM) Then
Ch11_y2 = Deflection * Pixels_Per_MM
ElseIf Ch11_y2 < (-1 * (Deflection * Pixels_Per_MM)) Then
Ch11_y2 = (Deflection * Pixels_Per_MM) * -1
End If
Ch12_y2 = Intfilterbuffer(n, 11)
If Ch12_y2 > (Deflection * Pixels_Per_MM) Then
Ch12_y2 = Deflection * Pixels_Per_MM
ElseIf Ch12_y2 < (-1 * (Deflection * Pixels_Per_MM)) Then
Ch12_y2 = (Deflection * Pixels_Per_MM) * -1
End If
Ch13_y2 = Intfilterbuffer(n, 12)
If Ch13_y2 > (Deflection * Pixels_Per_MM) Then
Ch13_y2 = Deflection * Pixels_Per_MM
ElseIf Ch13_y2 < (-1 * (Deflection * Pixels_Per_MM)) Then
Ch13_y2 = (Deflection * Pixels_Per_MM) * -1
End If
Ch14_y2 = Intfilterbuffer(n, 13)
If Ch14_y2 > (Deflection * Pixels_Per_MM) Then
Ch14_y2 = Deflection * Pixels_Per_MM
ElseIf Ch14_y2 < (-1 * (Deflection * Pixels_Per_MM)) Then
Ch14_y2 = (Deflection * Pixels_Per_MM) * -1
End If
Ch15_y2 = Intfilterbuffer(n, 14)
If Ch15_y2 > (Deflection * Pixels_Per_MM) Then
Ch15_y2 = Deflection * Pixels_Per_MM
ElseIf Ch15_y2 < (-1 * (Deflection * Pixels_Per_MM)) Then
Ch15_y2 = (Deflection * Pixels_Per_MM) * -1
End If
Ch16_y2 = Intfilterbuffer(n, 15)
If Ch16_y2 > (Deflection * Pixels_Per_MM) Then
Ch16_y2 = Deflection * Pixels_Per_MM
ElseIf Ch16_y2 < (-1 * (Deflection * Pixels_Per_MM)) Then
Ch16_y2 = (Deflection * Pixels_Per_MM) * -1
End If
O1currentX = O1lastX + Sweep_factor
Ch1_y2 = Ch1_y2 + 1 * Channel_Area
Ch2_y2 = Ch2_y2 + 2 * Channel_Area
Ch3_y2 = Ch3_y2 + 3 * Channel_Area
Ch4_y2 = Ch4_y2 + 4 * Channel_Area
Ch5_y2 = Ch5_y2 + 5 * Channel_Area
Ch6_y2 = Ch6_y2 + 6 * Channel_Area
Ch7_y2 = Ch7_y2 + 7 * Channel_Area
Ch8_y2 = Ch8_y2 + 8 * Channel_Area
Ch9_y2 = Ch9_y2 + 9 * Channel_Area
Ch10_y2 = Ch10_y2 + 10 * Channel_Area
Ch11_y2 = Ch11_y2 + 11 * Channel_Area
Ch12_y2 = Ch12_y2 + 12 * Channel_Area
Ch13_y2 = Ch13_y2 + 13 * Channel_Area
Ch14_y2 = Ch14_y2 + 14 * Channel_Area
Ch15_y2 = Ch15_y2 + 15 * Channel_Area
Ch16_y2 = Ch16_y2 + 16 * Channel_Area
p2.Color = Label2.ForeColor
mygraphics.DrawLine(p2, O1lastX, Ch1_y1, O1currentX, Ch1_y2)
p2.Color = Label3.ForeColor
mygraphics.DrawLine(p2, O1lastX, Ch2_y1, O1currentX, Ch2_y2)
p2.Color = Label4.ForeColor
mygraphics.DrawLine(p2, O1lastX, Ch3_y1, O1currentX, Ch3_y2)
p2.Color = Label5.ForeColor
mygraphics.DrawLine(p2, O1lastX, Ch4_y1, O1currentX, Ch4_y2)
p2.Color = Label6.ForeColor
mygraphics.DrawLine(p2, O1lastX, Ch5_y1, O1currentX, Ch5_y2)
p2.Color = Label7.ForeColor
mygraphics.DrawLine(p2, O1lastX, Ch6_y1, O1currentX, Ch6_y2)
p2.Color = Label8.ForeColor
mygraphics.DrawLine(p2, O1lastX, Ch7_y1, O1currentX, Ch7_y2)
p2.Color = Label9.ForeColor
mygraphics.DrawLine(p2, O1lastX, Ch8_y1, O1currentX, Ch8_y2)
p2.Color = Label10.ForeColor
mygraphics.DrawLine(p2, O1lastX, Ch9_y1, O1currentX, Ch9_y2)
p2.Color = Label11.ForeColor
mygraphics.DrawLine(p2, O1lastX, Ch10_y1, O1currentX, Ch10_y2)
p2.Color = Label12.ForeColor
mygraphics.DrawLine(p2, O1lastX, Ch11_y1, O1currentX, Ch11_y2)
p2.Color = Label13.ForeColor
mygraphics.DrawLine(p2, O1lastX, Ch12_y1, O1currentX, Ch12_y2)
p2.Color = Label14.ForeColor
mygraphics.DrawLine(p2, O1lastX, Ch13_y1, O1currentX, Ch13_y2)
p2.Color = Label15.ForeColor
mygraphics.DrawLine(p2, O1lastX, Ch14_y1, O1currentX, Ch14_y2)
p2.Color = Label16.ForeColor
mygraphics.DrawLine(p2, O1lastX, Ch15_y1, O1currentX, Ch15_y2)
p2.Color = Label17.ForeColor
mygraphics.DrawLine(p2, O1lastX, Ch16_y1, O1currentX, Ch16_y2)
Ch1_y1 = Ch1_y2
Ch2_y1 = Ch2_y2
Ch3_y1 = Ch3_y2
Ch4_y1 = Ch4_y2
Ch5_y1 = Ch5_y2
Ch6_y1 = Ch6_y2
Ch7_y1 = Ch7_y2
Ch8_y1 = Ch8_y2
Ch9_y1 = Ch9_y2
Ch10_y1 = Ch10_y2
Ch11_y1 = Ch11_y2
Ch12_y1 = Ch12_y2
Ch13_y1 = Ch13_y2
Ch14_y1 = Ch14_y2
Ch15_y1 = Ch15_y2
Ch16_y1 = Ch16_y2
O1lastX = O1currentX
If O1lastX >= Plotting_Panel.Width Then
O1currentX = 0
O1lastX = 0
mygraphics.Clear(Color.LightYellow)
Plotting_Panel.Invalidate()
End If
Next
End If
End Function
-
Sep 10th, 2020, 12:54 AM
#21
Thread Starter
Member
-
Sep 10th, 2020, 06:12 AM
#22
Re: How to Handle Serial data event and new frame event using Aforge library at same
Since you're drawing into a bitmap, you could do all that drawing in a background thread.
You would only need to invoke when you wanted to update the panel with the result.
Your drawing process is the really slow part of this application, so I'm sure you're receiving serial data faster than you can draw it. When you use BeginInvoke, the drawing looks good, but I think it is really falling further and further behind the data being received, i.e. it lags the realtime data more and more as time goes on. The GUI thread is overloaded, which is why it is slow, as it draws as fast as it can so is using up almost all of the its time just doing the drawing.
When you use invoke, then the receiving thread is tied to the GUI, so your receiving thread is slowed down, to the speed that you can do your drawing at, which is slower than the rate you are receiving data, so you end up loosing data, but the GUI is responsive because you don't end up with a backlog of drawing events stacking up on the GUI thread.
Even the following example which is not doing a lot of drawing, takes longer to draw than the number of values being generated by the simulated ReadLine code.
When the code finishes updating the panel, and loops back to wait, it doesn't really wait, because there are already five or more items in the concurrent queue that were enqueued during the time it was drawing.
Anyway, here is the code I was testing with. I create the panel in code, so you don't have to create the panel in the IDE to run the example. Just paste the code in a new project and run.
Code:
Imports System.Threading
Public Class Form1
Private ReadThread As New Thread(AddressOf ReadThreadProc)
Private DrawThread As New Thread(AddressOf DrawThreadProc)
Private DrawWait As New AutoResetEvent(False)
Private panelBitMap As New Bitmap(10, 10)
Private drawBitMap As New Bitmap(10, 10)
Private WithEvents panel1 As New Panel
Private rand As New Random
Private dataQueue As New Collections.Concurrent.ConcurrentQueue(Of Integer)
Private running As Boolean = True
Private Sub ReadThreadProc()
Dim datapacket As Integer
Dim dataString As String
Dim rcounter As Long
Do While running
'dataString = SerialPort1.Readline
'simulate SerialPort1.readline
dataString = (100 - rand.Next(200)).ToString
Thread.Sleep(1)
'end of serialport1.readline emulation
datapacket = Integer.Parse(dataString)
dataQueue.Enqueue(datapacket)
' rcounter += 1
' Debug.Print("rcount {0}", rcounter.ToString)
DrawWait.Set()
Loop
End Sub
Private Sub DrawThreadProc()
Dim X As Integer, lastX As Integer
Dim Y As Integer, lastY As Integer
Dim g As Graphics
Dim dcounter As Long
Dim qcount As Integer
Do
DrawWait.WaitOne()
' dcounter += 1
' Debug.Print("dcount {0}", dcounter.ToString)
If running Then
g = Graphics.FromImage(drawBitMap)
Do While dataQueue.TryDequeue(Y)
qcount += 1
X += 1
If X > drawBitMap.Width Then
X = 0
lastX = -1
g.Clear(Color.LightYellow)
End If
g.DrawLine(Pens.Blue, lastX, lastY + 200, X, Y + 200)
lastX = X
lastY = Y
Loop
' Debug.Print("qcount {0}", qcount.ToString)
' qcount = 0
panelBitMap.Dispose()
panelBitMap = CType(drawBitMap.Clone, Bitmap)
Me.Invoke(Sub()
Using gp As Graphics = panel1.CreateGraphics
gp.DrawImage(panelBitMap, 0, 0)
End Using
End Sub)
End If
Loop While running
Me.Invoke(Sub() Me.Close()) 'allow the form to close now
End Sub
Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
If running Then
e.Cancel = True
End If
running = False 'let threads exit, in particular the one invoking back to the form
End Sub
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Controls.Add(panel1)
panel1.Bounds = (New Rectangle(10, 10, 600, 400))
drawBitMap.Dispose()
drawBitMap = New Bitmap(panel1.ClientSize.Width, panel1.ClientSize.Height)
Using g = Graphics.FromImage(drawBitMap)
g.Clear(Color.LightYellow)
End Using
DrawThread.IsBackground = True
DrawThread.Start()
ReadThread.IsBackground = True
ReadThread.Start()
End Sub
End Class
You didn't show how you took the data input, and spread it across your filter buffers to get 16 lines, but if you do all that and the drawing in a background thread, and only do the final panel update by invoking, your GUI should be a lot more responsive.
"Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930
-
Sep 10th, 2020, 07:35 AM
#23
Thread Starter
Member
Re: How to Handle Serial data event and new frame event using Aforge library at same
Hi,
Where Iam using Me.Invoke((New MyDelegate(AddressOf DrawGraph2)), datapacket), This is passing my data to drawgraph2 function which is running on Main UI thread. So instead of passing data to drawgraph2 I need to pass data to a another background thread.
Instead of using me.invoke I simple called DrawGraph2(datapacket). I used another thread called Draw_Function_For_Thread and start it in form load.
But It goes inside the thread only once during load. There after it never goes inside the new thread which I created for drawing bitmap and invoking the bitmap to main UI Thread.
I tried below code
Code:
Private DrawThread As New Thread(AddressOf Draw_Function_For_Thread)
Private ReadSerialPortTask As task
Private Sub Acquire_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim monitorWidth As Integer = My.Computer.Screen.WorkingArea.Size.Width
Dim monitorHeight As Integer = My.Computer.Screen.WorkingArea.Size.Height
' --- adjust size of this form ** this does NOT make consideration of DPI (eg. 125%, 150%) **
Me.Width = monitorWidth
Me.Height = monitorHeight
Control.CheckForIllegalCrossThreadCalls = False
Plotting_Signal = New Bitmap(Plotting_Panel.Width, Plotting_Panel.Height)
Dim g As Graphics = Me.CreateGraphics
Monitor_PPI_On_Y = g.DpiY
Pixels_Per_MM = Monitor_PPI_On_Y / 25.4
DrawThread.IsBackground = True
DrawThread.Start()
End Sub
Private Sub SerialPort1_DataReceived(sender As Object, e As IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
ReadWait.Set() 'cause ReadSerialPort to run
End Sub
Private ReadWait As New Threading.AutoResetEvent(False)
Private Sub ReadSerialPort()
'
'this is run as a Background task
'
Dim data As New System.Text.StringBuilder(1024 * 1024) 'accumulate SerialPort data here
Dim mess As New System.Text.StringBuilder 'individual message
Dim foundMess As Boolean = False
Dim er As Integer
Do
ReadWait.WaitOne() 'wait for SerialPort1.DataReceived
Do While SerialPort1.BytesToRead > 0 'accumulate all available bytes
data.Append(SerialPort1.ReadExisting)
Loop
Do 'process message, if one available
mess.Length = 0 'reset message
foundMess = False 'not found
Dim idx As Integer = 0
For idx = 0 To data.Length - 1 'look for CR??????
'data1(er) = data(idx)
If data(idx) = Constants.vbLf Then '<<<<<<<<<<<<<<<<<<<<<<<
' 'note that the CR will NOT be in mess
foundMess = True 'found message
Exit For
' 'ElseIf data(idx) = ControlChars.Lf Then
' ' Continue For
End If
mess.Append(data(idx))
Next
If foundMess Then
data.Remove(0, idx + 1) 'remove found message from data
'
'process your message here <<<<<<<<<<<<
'
Dim datapacket As Integer
If Integer.TryParse(mess.ToString, datapacket) Then
' Me.Invoke((New MyDelegate(AddressOf DrawGraph2)), datapacket)
DrawGraph2(datapacket)
End If
End If
Loop While foundMess AndAlso data.Length > 0
Loop
End Sub
Private Sub DrawGraph2(XVALUE1 As Integer)
If transmission = "a" Then
Plot_Signal = True
End Sub
Public Sub Draw_Function_For_Thread()
If Plot_Signal = True Then
Plot_Signal_Picture()
Me.Invoke(Sub()
mygraphics = Plotting_Panel.CreateGraphics
mygraphics.DrawImage(Plotting_Signal, 0, 0)
End Sub)
Plot_Signal = False
End If
End Sub
-
Sep 10th, 2020, 02:34 PM
#24
Re: How to Handle Serial data event and new frame event using Aforge library at same
There is no loop in your Draw_Function_For_Thread so you start the thread, it runs through and exits and that is the end of the thread.
You used ReadWait to pause your SerialPort reading thread, until you had data, likewise in my example I used DrawWait to pause the Drawing thread until I had data to draw. In both those threads, you see that code is inside a loop, so the thread continues to loop, it doesn't exit.
Instead of using a boolean, Plot_Signal, to try to trigger the thread, you need to wait on something (in this case, we're using a Threading.AutoResetEvent object to do the waiting).
You also need a loop so you don't exit the thread, just loop back up and wait for the next event.
In my case, I passed the value in a queue rather than a parameter, because it allows passing more than one value at a time (in a sense), especially in the case where it takes so long to draw.
You probably want to change your drawing so that you can add all the values you receive to your channel filter arrays and only draw when you don't have any more values pending from your serial stream. That way you can keep up with the serial data. You probably don't realize that while the data looks good, it is probably lagging the serial stream by more and more as time passes.
I've had the issue before where the data we displayed fell slowly behind the data that was being generated and we didn't realize it until we noticed the lag in reaction of what was being displayed to control inputs which should have had immediate effect on the visual display.
"Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930
-
Sep 13th, 2020, 02:52 AM
#25
Thread Starter
Member
Re: How to Handle Serial data event and new frame event using Aforge library at same
Hi,
I tried the code using background thread for processing all the data and only invoking the main UI once after all the processing. But the problem is that the background thread is really slow as compared to the main UI Thread. The same data processing and plotting is fast in the Main UI thread as compared to the same work done in background thread. I also tried to increase the thread priority to highest level , but still It is very slow.
When I plot in the Main UI Thread I see instant real time change in signal, But the same work in background thread the real time change is not seen and it keeps lagging further and further.
Below is the code I tried. I'am posting the complete code. I have cut short my data plotting code as short as I could.
Code:
Private DrawThread As New Thread(AddressOf Draw_Function_For_Thread)
Private ReadSerialPortTask As Task
Private ReadWait As New Threading.AutoResetEvent(False)
Private dataQueue As New Collections.Concurrent.ConcurrentQueue(Of Integer)
public boolean running = false
Private Sub Acquire_Load(sender As Object, e As EventArgs) Handles MyBase.Load
//other initilizatons
running = true
DrawThread.Priority = ThreadPriorityLevel.Highest
DrawThread.IsBackground = True
DrawThread.Start()
End Sub
Private Sub Acquire_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
ReadSerialPortTask = Task.Factory.StartNew(Sub() ReadSerialPort())
End Sub
Private Sub SerialPort1_DataReceived(sender As Object, e As IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
ReadWait.Set() 'cause ReadSerialPort to run
End Sub
Private Sub ReadSerialPort()
'
'this is run as a Background task
'
Dim data As New System.Text.StringBuilder(1024 * 1024) 'accumulate SerialPort data here
' Dim data1(1024 * 1024) As String
' Dim data2(1024 * 1024) As String
Dim mess As New System.Text.StringBuilder 'individual message
Dim foundMess As Boolean = False
Dim er As Integer
Do
ReadWait.WaitOne() 'wait for SerialPort1.DataReceived
Do While SerialPort1.BytesToRead > 0 'accumulate all available bytes
data.Append(SerialPort1.ReadExisting)
Loop
Do 'process message, if one available
mess.Length = 0 'reset message
foundMess = False 'not found
Dim idx As Integer = 0
For idx = 0 To data.Length - 1 'look for CR??????
If data(idx) = Constants.vbLf Then '<<<<<<<<<<<<<<<<<<<<<<<
' 'note that the CR will NOT be in mess
foundMess = True 'found message
Exit For
End If
mess.Append(data(idx))
Next
If foundMess Then
data.Remove(0, idx + 1) 'remove found message from data
'
'process your message here <<<<<<<<<<<<
'
Dim datapacket As Integer
If Integer.TryParse(mess.ToString, datapacket) Then
dataQueue.Enqueue(datapacket)
End If
End If
'look for more messages in data?
Loop While foundMess AndAlso data.Length > 0
Loop
End Sub
Private Sub Draw_Function_For_Thread()
Do
Do While dataQueue.TryDequeue(ssst)
DrawGraph2(datapacket)
If Plot_Signal = True Then
Plot_Signal = False
Me.Invoke(Sub()
Using gp As Graphics = Plotting_Panel.CreateGraphics
gp.DrawImage(Plotting_Signal, 0, 0)
End Using
End Sub)
Dim second As Long = seconds
Plotting_Signal.Dispose()
Plotting_Signal = New Bitmap(Plotting_Panel.Width, Plotting_Panel.Height)
'''''For Minor Grid
If Minor_grid = True Then
If Sweep7_5 = True Then
Dim Minor_gridFactor As Single = Plotting_Panel.Width / (40 * 5)
Dim Minor_gridPen As New Pen(Color.LightGray, 1)
Dim i As Double
mygraphics = Graphics.FromImage(Plotting_Signal)
For i = 0 To Plotting_Panel.Width Step Minor_gridFactor
mygraphics.DrawLine(Minor_gridPen, CInt(i), 0, CInt(i), Plotting_Panel.Height)
Next
ElseIf Sweep15 = True Then
Dim Minor_gridFactor As Single = Plotting_Panel.Width / (20 * 5)
Dim Minor_gridPen As New Pen(Color.LightGray, 1)
Dim i As Double
mygraphics = Graphics.FromImage(Plotting_Signal)
For i = 0 To Plotting_Panel.Width Step Minor_gridFactor
mygraphics.DrawLine(Minor_gridPen, CInt(i), 0, CInt(i), Plotting_Panel.Height)
Next
ElseIf Sweep30 = True Then
Dim Minor_gridFactor As Single = Plotting_Panel.Width / (10 * 5)
Dim Minor_gridPen As New Pen(Color.LightGray, 1)
Dim i As Double
mygraphics = Graphics.FromImage(Plotting_Signal)
For i = 0 To Plotting_Panel.Width Step Minor_gridFactor
mygraphics.DrawLine(Minor_gridPen, CInt(i), 0, CInt(i), Plotting_Panel.Height)
Next
ElseIf Sweep60 = True Then
Dim Minor_gridFactor As Single = Plotting_Panel.Width / (5 * 5)
Dim Minor_gridPen As New Pen(Color.LightGray, 1)
Dim i As Double
mygraphics = Graphics.FromImage(Plotting_Signal)
For i = 0 To Plotting_Panel.Width Step Minor_gridFactor
mygraphics.DrawLine(Minor_gridPen, CInt(i), 0, CInt(i), Plotting_Panel.Height)
Next
End If
End If
''''For Majjor Grid
If Major_grid = True Then
Select Case True
Case Sweep7_5
mygraphics = Graphics.FromImage(Plotting_Signal)
Dim Major_gridFactor As Single = Plotting_Panel.Width / 40
Dim Major_gridPen As New Pen(Color.Black, 1)
Dim brush As New SolidBrush(Color.Black)
Dim TimeFont As New Font("Arial", 13)
Dim i As Double
For i = 0 To Plotting_Panel.Width Step Major_gridFactor
mygraphics.DrawLine(Major_gridPen, CInt(i), 0, CInt(i), Plotting_Panel.Height)
second = second + 1
mygraphics.DrawString(TimeDisplay(second), TimeFont, brush, (i + 1), (Plotting_Panel.Height - 50))
Next
Case Sweep15
mygraphics = Graphics.FromImage(Plotting_Signal)
Dim Major_gridFactor As Single = Plotting_Panel.Width / 20
Dim Major_gridPen As New Pen(Color.Black, 1)
Dim brush As New SolidBrush(Color.Black)
Dim TimeFont As New Font("Arial", 13)
Dim i As Double
For i = 0 To Plotting_Panel.Width Step Major_gridFactor
mygraphics.DrawLine(Major_gridPen, CInt(i), 0, CInt(i), Plotting_Panel.Height)
second = second + 1
mygraphics.DrawString(TimeDisplay(second), TimeFont, brush, (i + 1), (Plotting_Panel.Height - 50))
Next
Case Sweep30
mygraphics = Graphics.FromImage(Plotting_Signal)
Dim Major_gridFactor As Single = Plotting_Panel.Width / 10
Dim Major_gridPen As New Pen(Color.Black, 1)
Dim brush As New SolidBrush(Color.Black)
Dim TimeFont As New Font("Arial", 13)
Dim i As Double
For i = 0 To Plotting_Panel.Width Step Major_gridFactor
mygraphics.DrawLine(Major_gridPen, CInt(i), 0, CInt(i), Plotting_Panel.Height)
second = second + 1
mygraphics.DrawString(TimeDisplay(second), TimeFont, brush, (i + 1), (Plotting_Panel.Height - 50))
Next
Case Sweep60
mygraphics = Graphics.FromImage(Plotting_Signal)
Dim Major_gridFactor As Single = Plotting_Panel.Width / 5
Dim Major_gridPen As New Pen(Color.Black, 1)
Dim brush As New SolidBrush(Color.Black)
Dim TimeFont As New Font("Arial", 13)
Dim i As Double
For i = 0 To Plotting_Panel.Width Step Major_gridFactor
mygraphics.DrawLine(Major_gridPen, CInt(i), 0, CInt(i), Plotting_Panel.Height)
second = second + 1
mygraphics.DrawString(TimeDisplay(second), TimeFont, brush, (i + 1), (Plotting_Panel.Height - 50))
Next
End Select
End If
End If
Loop
' End If
Loop While running
Me.Invoke(Sub() Me.Close()) 'allow the form to close now
End Sub
Private Sub DrawGraph2(XVALUE1 As Integer)
For sample = 0 To gMAX_SAMPLES - 1 ' no. of data values
Plot_channel = Main_Panel_Channels_To_Display
For channel = 0 To Plot_channel - 1 ' no. of lines to display
// data processing to obtain final value for filter buffer
mygraphics = Graphics.FromImage(Plotting_Signal)
mygraphics.PageUnit = GraphicsUnit.Pixel
mygraphics.PageScale = 1
mygraphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
mygraphics.ScaleTransform(1, 1)
Ch1_y2 = Intfilterbuffer(sample, channel)
If Ch1_y2 > (Deflection * Pixels_Per_MM) Then
Ch1_y2 = Deflection * Pixels_Per_MM
ElseIf Ch1_y2 < (-1 * (Deflection * Pixels_Per_MM)) Then
Ch1_y2 = (Deflection * Pixels_Per_MM) * -1
End If
O1currentX = O1lastX + Sweep_factor
Ch1_y2 = Ch1_y2 + (channel + 1) * Channel_Area
p2.Color = panel1.Controls("Label" & (channel + 2).ToString).ForeColor
mygraphics.DrawLine(p2, O1lastX, Ch1_y1(channel), O1currentX, Ch1_y2)
Ch1_y1(channel) = Ch1_y2
'Me.Invoke(Sub()
' Using gp As Graphics = Plotting_Panel.CreateGraphics
' gp.DrawImage(Plotting_Signal, 0, 0)
' End Using
' End Sub)
Next
O1lastX = O1currentX
If O1lastX >= Plotting_Panel.Width Then
O1currentX = 0
O1lastX = 0
mygraphics.Clear(Color.LightYellow)
'Plotting_Signal.Dispose()
'Plotting_Signal = New Bitmap(Plotting_Panel.Width, Plotting_Panel.Height)
Plotting_Panel.Invalidate()
End If
Next
If transmission = "a" Then
Plot_Signal = True
'Using gp As Graphics = Plotting_Panel.CreateGraphics
' gp.DrawImage(Plotting_Signal, 0, 0)
'End Using
End If
End Sub
-
Sep 14th, 2020, 10:06 AM
#26
Re: How to Handle Serial data event and new frame event using Aforge library at same
If that is the complete code, then I guess you're out of luck.
The following won't compile in any version of VB I tried.
public boolean running = false
The following is also a syntax error in VB 2010 and 2017 (I don't have 2015)
//other initializatons
Also you have the line
DrawThread.Priority = ThreadPriorityLevel.Highest
That doesn't make sense, and shouldn't be allowed, but you obviously don't have Option Strict On, so are allowing invalid coercion of types to take place.
What you meant is:
DrawThread.Priority = ThreadPriority.Highest
That should have been provided to you in a list by intellisense in the IDE.
ThreadPriority.Highest = 4
ThreadPriorityLevel.Highest = 2
So, you code is setting DrawThread.Priority to 2, not 4. 2 = ThreadPriority.Normal.
Although, it probably wouldn't make much difference. You shouldn't be changing ThreadPrioirty in general anyway, and you can't expect it to make up for bad code.
transmission isn't defined anywhere, nor is it set anywhere else in the code, this is the only reference to it.
Code:
If transmission = "a" Then
Plot_Signal = True
That is the only place Plot_Signal is set to True, so it must never be true, unless you have another module where these things are defined and manipulated.
If Plot_Signal is not true, almost none of the stuff in the DrawFunction thread is done.
You enqueue your Serial Data into the dataQueue, but when you dequeue, you don't use what you might have dequeued.
You call DrawGraph2 with the current value of datapacket, which means you may have dropped some, or you use the same latest value of datapacket multiple times (how ever many versions of datapacket were queued.
For instance, lets say while you were drawing, the numbers 1, 2, 3, 4, 5 were received and queued in the queue.
When you loop back to the top and start to dequeue, datapacket is set to 5, i.e. the last value received.
You dequeue the 1, but then draw using datapacket, so you draw 5.
You dequeue 2, and you draw 5.
You dequeue 3, and you draw 5
You dequeue 4, and you draw 5
You dequeue 5, and you draw 5
I don't see Plotting_Signal declared anywhere.
If Plotting_Signal was a bitmap, then I would be surprised you see any drawing because I don't see that the bitmap is drawn in any code that get's executed, i.e. where it is invoked is inside the "If Plot_Signal = True", which doesn't ever get set in the code you posted.
You create a lot of graphical objects, i.e. graphic object, pens and brushes which you never dispose of. Also, you create copies of stock pens and brushes, so those are things you don't even have to create and dispose of. You can just use the pre-existing versions in the Pens and Brushes classes.
Perhaps rather than continue to throw code at the problem, a discussion of design would be more productive. What is the environment you are dealing with, i.e. approximately how many points are you receiving from the Serial port per second? How do you determine how many traces to drawn, and how is the data coming in the Serial port assigned to a specific trace? I don't think the drawing should be refreshed to the screen for every point received, but perhaps is should be refreshed everytime you have all the points for all the traces for a specific time.
If you could just update the latest portion of the traces, rather than the whole graph, that should be faster.
If the drawing appears to be drawing slower and falling behind, compared to when you were drawing directly to the panel, it could be a perception thing, because your not updating the panel from your drawing as often as you should be. Or it could be when you were drawing to the panel directly, you were drawing the latest data you received, losing some intermediate data, so because you're drawing lines, you end up with a complete connected line in the graph, but it has essentially dropped some of the points in the line, so has in essence filtered out higher frequency data, drawing the graph at a lower resolution, allowing it to keep up with the data stream, although taking most of your GUI thread time to draw what it did draw.
I don't know that I have the time to give you the help you need. I think the drawing needs to be more efficient, but it is hard to give good examples without knowing details of your data stream and how it is processed to give you plotting data. Knowing the speed of the data, i.e. the number of points per second you are receiving that need to be plotted would probably be a good reference so that a test case of feeding a stream of simulated data at that speed and plotting it could be used as a representative test case for coding examples.
"Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930
-
Sep 14th, 2020, 02:49 PM
#27
Re: How to Handle Serial data event and new frame event using Aforge library at same
Some changes, pay attention to haveDataPacket and its use. Agree with passel.
Code:
Private DrawThread As New Thread(AddressOf Draw_Function_For_Thread)
Private ReadSerialPortTask As Task
Private ReadWait As New Threading.AutoResetEvent(False)
Private dataQueue As New Collections.Concurrent.ConcurrentQueue(Of Integer)
Public running As Boolean = False
Private Sub Acquire_Load(sender As Object, e As EventArgs) Handles MyBase.Load
''other initilizatons
running = True
'DrawThread.Priority = ThreadPriorityLevel.Highest
DrawThread.IsBackground = True
DrawThread.Start()
End Sub
Private Sub Acquire_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
ReadSerialPortTask = Task.Factory.StartNew(Sub() ReadSerialPort())
End Sub
Private Sub SerialPort1_DataReceived(sender As Object, e As IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
ReadWait.Set() 'cause ReadSerialPort to run
End Sub
Private Sub ReadSerialPort()
'
'this is run as a Background task
'
Dim data As New System.Text.StringBuilder(1024 * 1024) 'accumulate SerialPort data here
' Dim data1(1024 * 1024) As String
' Dim data2(1024 * 1024) As String
Dim mess As New System.Text.StringBuilder 'individual message
Dim foundMess As Boolean = False
Dim er As Integer
Do
ReadWait.WaitOne() 'wait for SerialPort1.DataReceived
Do While SerialPort1.BytesToRead > 0 'accumulate all available bytes
data.Append(SerialPort1.ReadExisting)
Loop
Do 'process message, if one available
mess.Length = 0 'reset message
foundMess = False 'not found
Dim idx As Integer = 0
For idx = 0 To data.Length - 1 'look for CR??????
If data(idx) = Constants.vbLf Then '<<<<<<<<<<<<<<<<<<<<<<<
' 'note that the CR will NOT be in mess
foundMess = True 'found message
Exit For
End If
mess.Append(data(idx))
Next
If foundMess Then
data.Remove(0, idx + 1) 'remove found message from data
'
'process your message here <<<<<<<<<<<<
'
Dim datapacket As Integer
If Integer.TryParse(mess.ToString, datapacket) Then
dataQueue.Enqueue(datapacket)
haveDataPacket.Set() '<<<<<<<<<<<<<<<
End If
End If
'look for more messages in data?
Loop While foundMess AndAlso data.Length > 0
Loop
End Sub
Private haveDataPacket As New Threading.ManualResetEvent(False) '<<<<<<<<<<<<<<<
Private Sub Draw_Function_For_Thread()
Do
haveDataPacket.WaitOne() '<<<<<<<<<<<<<<<
haveDataPacket.Reset() '<<<<<<<<<<<<<<<
Do While dataQueue.TryDequeue(ssst)
DrawGraph2(datapacket)
If Plot_Signal = True Then
Plot_Signal = False
Me.Invoke(Sub()
Using gp As Graphics = Plotting_Panel.CreateGraphics
gp.DrawImage(Plotting_Signal, 0, 0)
End Using
End Sub)
Dim second As Long = seconds
Plotting_Signal.Dispose()
Plotting_Signal = New Bitmap(Plotting_Panel.Width, Plotting_Panel.Height)
'''''For Minor Grid
If Minor_grid = True Then
If Sweep7_5 = True Then
Dim Minor_gridFactor As Single = Plotting_Panel.Width / (40 * 5)
Dim Minor_gridPen As New Pen(Color.LightGray, 1)
Dim i As Double
mygraphics = Graphics.FromImage(Plotting_Signal)
For i = 0 To Plotting_Panel.Width Step Minor_gridFactor
mygraphics.DrawLine(Minor_gridPen, CInt(i), 0, CInt(i), Plotting_Panel.Height)
Next
ElseIf Sweep15 = True Then
Dim Minor_gridFactor As Single = Plotting_Panel.Width / (20 * 5)
Dim Minor_gridPen As New Pen(Color.LightGray, 1)
Dim i As Double
mygraphics = Graphics.FromImage(Plotting_Signal)
For i = 0 To Plotting_Panel.Width Step Minor_gridFactor
mygraphics.DrawLine(Minor_gridPen, CInt(i), 0, CInt(i), Plotting_Panel.Height)
Next
ElseIf Sweep30 = True Then
Dim Minor_gridFactor As Single = Plotting_Panel.Width / (10 * 5)
Dim Minor_gridPen As New Pen(Color.LightGray, 1)
Dim i As Double
mygraphics = Graphics.FromImage(Plotting_Signal)
For i = 0 To Plotting_Panel.Width Step Minor_gridFactor
mygraphics.DrawLine(Minor_gridPen, CInt(i), 0, CInt(i), Plotting_Panel.Height)
Next
ElseIf Sweep60 = True Then
Dim Minor_gridFactor As Single = Plotting_Panel.Width / (5 * 5)
Dim Minor_gridPen As New Pen(Color.LightGray, 1)
Dim i As Double
mygraphics = Graphics.FromImage(Plotting_Signal)
For i = 0 To Plotting_Panel.Width Step Minor_gridFactor
mygraphics.DrawLine(Minor_gridPen, CInt(i), 0, CInt(i), Plotting_Panel.Height)
Next
End If
End If
''''For Majjor Grid
If Major_grid = True Then
Select Case True
Case Sweep7_5
mygraphics = Graphics.FromImage(Plotting_Signal)
Dim Major_gridFactor As Single = Plotting_Panel.Width / 40
Dim Major_gridPen As New Pen(Color.Black, 1)
Dim brush As New SolidBrush(Color.Black)
Dim TimeFont As New Font("Arial", 13)
Dim i As Double
For i = 0 To Plotting_Panel.Width Step Major_gridFactor
mygraphics.DrawLine(Major_gridPen, CInt(i), 0, CInt(i), Plotting_Panel.Height)
second = second + 1
mygraphics.DrawString(TimeDisplay(second), TimeFont, brush, (i + 1), (Plotting_Panel.Height - 50))
Next
Case Sweep15
mygraphics = Graphics.FromImage(Plotting_Signal)
Dim Major_gridFactor As Single = Plotting_Panel.Width / 20
Dim Major_gridPen As New Pen(Color.Black, 1)
Dim brush As New SolidBrush(Color.Black)
Dim TimeFont As New Font("Arial", 13)
Dim i As Double
For i = 0 To Plotting_Panel.Width Step Major_gridFactor
mygraphics.DrawLine(Major_gridPen, CInt(i), 0, CInt(i), Plotting_Panel.Height)
second = second + 1
mygraphics.DrawString(TimeDisplay(second), TimeFont, brush, (i + 1), (Plotting_Panel.Height - 50))
Next
Case Sweep30
mygraphics = Graphics.FromImage(Plotting_Signal)
Dim Major_gridFactor As Single = Plotting_Panel.Width / 10
Dim Major_gridPen As New Pen(Color.Black, 1)
Dim brush As New SolidBrush(Color.Black)
Dim TimeFont As New Font("Arial", 13)
Dim i As Double
For i = 0 To Plotting_Panel.Width Step Major_gridFactor
mygraphics.DrawLine(Major_gridPen, CInt(i), 0, CInt(i), Plotting_Panel.Height)
second = second + 1
mygraphics.DrawString(TimeDisplay(second), TimeFont, brush, (i + 1), (Plotting_Panel.Height - 50))
Next
Case Sweep60
mygraphics = Graphics.FromImage(Plotting_Signal)
Dim Major_gridFactor As Single = Plotting_Panel.Width / 5
Dim Major_gridPen As New Pen(Color.Black, 1)
Dim brush As New SolidBrush(Color.Black)
Dim TimeFont As New Font("Arial", 13)
Dim i As Double
For i = 0 To Plotting_Panel.Width Step Major_gridFactor
mygraphics.DrawLine(Major_gridPen, CInt(i), 0, CInt(i), Plotting_Panel.Height)
second = second + 1
mygraphics.DrawString(TimeDisplay(second), TimeFont, brush, (i + 1), (Plotting_Panel.Height - 50))
Next
End Select
End If
End If
Loop
Loop While running
Me.Invoke(Sub() Me.Close()) 'allow the form to close now
End Sub
Private Sub DrawGraph2(XVALUE1 As Integer)
For sample = 0 To gMAX_SAMPLES - 1 ' no. of data values
Plot_channel = Main_Panel_Channels_To_Display
For channel = 0 To Plot_channel - 1 ' no. of lines to display
// data processing to obtain final value for filter buffer
mygraphics = Graphics.FromImage(Plotting_Signal)
mygraphics.PageUnit = GraphicsUnit.Pixel
mygraphics.PageScale = 1
mygraphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
mygraphics.ScaleTransform(1, 1)
Ch1_y2 = Intfilterbuffer(sample, channel)
If Ch1_y2 > (Deflection * Pixels_Per_MM) Then
Ch1_y2 = Deflection * Pixels_Per_MM
ElseIf Ch1_y2 < (-1 * (Deflection * Pixels_Per_MM)) Then
Ch1_y2 = (Deflection * Pixels_Per_MM) * -1
End If
O1currentX = O1lastX + Sweep_factor
Ch1_y2 = Ch1_y2 + (channel + 1) * Channel_Area
p2.Color = panel1.Controls("Label" & (channel + 2).ToString).ForeColor
mygraphics.DrawLine(p2, O1lastX, Ch1_y1(channel), O1currentX, Ch1_y2)
Ch1_y1(channel) = Ch1_y2
'Me.Invoke(Sub()
' Using gp As Graphics = Plotting_Panel.CreateGraphics
' gp.DrawImage(Plotting_Signal, 0, 0)
' End Using
' End Sub)
Next
O1lastX = O1currentX
If O1lastX >= Plotting_Panel.Width Then
O1currentX = 0
O1lastX = 0
mygraphics.Clear(Color.LightYellow)
'Plotting_Signal.Dispose()
'Plotting_Signal = New Bitmap(Plotting_Panel.Width, Plotting_Panel.Height)
Plotting_Panel.Invalidate()
End If
Next
If transmission = "a" Then
Plot_Signal = True
'Using gp As Graphics = Plotting_Panel.CreateGraphics
' gp.DrawImage(Plotting_Signal, 0, 0)
'End Using
End If
End Sub
-
Sep 18th, 2020, 07:05 AM
#28
Thread Starter
Member
Re: How to Handle Serial data event and new frame event using Aforge library at same
Hello Passel & dbasnett,
I was able to solve my problem by changing my data processing code which was happening before plotting.
Now both UI and signal plotting is real time.
thank you for suggestions which helped me resolve my issue.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|