OmarMallat

Having:

Microsoft windows server 2003

Visual Studio 2005 professional edition

Barcode Reader connected to COM1

when I open Hyper Terminal: 9600, 8, 1, None, I got the barcode if I pass any item in front of the Barcode Reader.

In visual studio 2005 Pro, I open serial port with same parameters but the DataReceived Event was not invoked.

I try DTR, RTS, timeout, read buffer and still cannot read from the serial port.

I also tried to read directly: str=com1.ReadExisting, ReadLine and nothing happen

any suggestions pleassssssssssssssssssssse




Re: .NET Base Class Library DataReceived not Invoked in SerialPort

nobugz

Post your code if you need help with this.





Re: .NET Base Class Library DataReceived not Invoked in SerialPort

OmarMallat

Ok. it was my fault, because the scanner has some missing parameters configuration.

It's now ok, but I have another problem. after somewhile of reading data, the console write the following error:

The thread 0x1fac has exited with code 0 (0x0).

and it repeat after each reading.

this is my code:

Private Sub c_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles c.DataReceived

Dim s As String = ""

s = c.ReadLine.TrimEnd

AddData(s)

End Sub

Private Sub AddData(ByVal bc As String)

If lblBarcode.InvokeRequired Then

lblBarcode.BeginInvoke(New AddDataCallback(AddressOf AddData), New Object() {bc})

Else

lblBarcode.Text = bc

Dim r As dsCounter.BarcodeRow

r = DsCounter.Barcode.FindByBarcodeNum(bc)

If r Is Nothing Then

r = DsCounter.Barcode.NewBarcodeRow

r.BarcodeNum = bc

r.BarcodeCount = 1

DsCounter.Barcode.Rows.Add(r)

Else

r.BarcodeCount += 1

End If

DsCounter.AcceptChanges()

End If

End Sub

Private Sub btnSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSave.Click

Try

Me.Validate()

Me.BarcodeBindingSource.EndEdit()

Me.BarcodeTableAdapter.Update(Me.DsCounter.Barcode)

MsgBox("Update successful")

Catch ex As Exception

MsgBox(ex.Message)

End Try

End Sub

  • the thread exit appear after 3 or 4 seconds, after the received data event occurs even if I don't call the sub AddData(s)
  • If I click save , it will save without error, but if I go the database I will find it empty.
  • if I try to change the value of barcode by editing the value in the datagridview dgvBarcode and I click save, it will save to database only this changes made manually.
  • I change my code to change the values of datagridview instead of changing the dataset tables values (in AddData sub), then when I click save, it save to database also.

Conclusion:

I got two differents errors:

1- thread exit message

2- changing the dataset will not affect the database, while changing the datagridview will affect the database. and note that when I click save, it didn't give me any exception (in the 2 cases)






Re: .NET Base Class Library DataReceived not Invoked in SerialPort

Carsten Kanstrup

You use AddData for two different routines. Invoke is probably always required so the BeginInvoke statement is always executed. This puts the event handler AddData for the DataReceived event on the message queue of the UI thread where it does not belong! In the BeginInvoke statement, the delegate should not point to AddData, but to a routine on the UI thread, which is used to empty the input buffer! Besides, you should not use BeginInvoke, but Invoke if you just marchal the call to a routine on the UI thread, which emties the receive buffer - see chapter "SerialPort Class" on this link: http://www.innovatic.dk/knowledg/SerialCOM/SerialCOM.htm

Also, be sure that you close the port somewhere in your code. If you don't, the automatic garbage collector of .NET may close the port for you while it is still in use.

Besides, In .NET, all strings are immutable, meaning you cannot change its length or contents once it is defined. Therefore, in the second statement of:

Dim s As String = ""

s = c.ReadLine.TrimEnd

you force .NET to make a new instance of the String class, copying the empty string and then abandon the previous string. Why not just combine the two statements to:

Dim s As String = c.ReadLine.TrimEnd





Re: .NET Base Class Library DataReceived not Invoked in SerialPort

nobugz

There is nothing wrong with the way you use BeginInvoke(). Also ignore the thread exit message, it is not an error, merely a notification from the debugger. Database drivers routinely spin up a temporary thread. I suspect the real problem is in your Dataset code. Try debugging it by calling AddData() from a temporary button's Click event handler.





Re: .NET Base Class Library DataReceived not Invoked in SerialPort

Carsten Kanstrup

It is indeed a strange construction with a subroutine, which invoke itself. When AddData is called from the event handler c_DataReceived, the BeginInvoke statement will always be activated, and when it is invoked by the message pump on the UI thread, the rest of the subroutine will run so why not just have two different routines

The problem with BeginInvoke in this case is that as soon as the AddDataCallback delegate (pointer to AddData) is added to the message queue by the BeginInvoke statement, the event handler c_DataReceived returns. When this happens, DataReceived is armed again and if the number of characters in the receive buffer is greater than the threshold level, DataReceived will fire again. This puts yet another instance of AddDataCallback on the message queue and this may continue at a very high rate (oscillation) until the number of bytes in the receive buffer gets below the threshold level. I have done some test where I have numbered the events and at moderate speeds (7200 bit/s as far as I remember) I have seen up to 20 completely unnecessary delegates on the message queue. Therefore, I do not recommend to use BeginInvoke if you empty the buffer from the UI thread instead of doing it in the event handler, so there is indeed something wrong with the way he uses BeginInvoke!





Re: .NET Base Class Library DataReceived not Invoked in SerialPort

nobugz

Nonsense. He only gets one DataReceived per barcode. Note the clever use of ReadLine().





Re: .NET Base Class Library DataReceived not Invoked in SerialPort

Carsten Kanstrup

Sorry, I can see that he indeed empties the buffer in the event handler (ReadLine) and then it is recommended to use BeginInvoke.



Re: .NET Base Class Library DataReceived not Invoked in SerialPort

OmarMallat

Ok, I changed my code.

I make new class clsSerial, I declare the serial port inside, and I raise an event from the serial data received event.

and from the main form I add an instance of my class and in the class event, I add data without delegate or thread or anything else. and it run properly.

about my old problem... the exit thread message come even I don't call AddData. it come just after s=c.readLine() was executed and not after I call AddData. but as you said, it don't matter.

but please let me know what is the problem in editing a row in a datatable using this code:

Dim r As dsCounter.BarcodeRow

r = DsCounter.Barcode.FindByBarcodeNum(bc)

If r Is Nothing Then

r = DsCounter.Barcode.NewBarcodeRow

r.BarcodeNum = bc

r.BarcodeCount = 1

DsCounter.Barcode.Rows.Add(r)

Else

r.BarcodeCount += 1

DsCounter.AcceptChanges()

End If

the add function run properly but the editing the the Else statment didn't do anything. why do you know how I solved the problem I have a bounded datagridview to this datatable, so if I need to edit a value in the datatable, I search it in the datagridview and I change it by code to succeed. it's very strange, but it works.




Re: .NET Base Class Library DataReceived not Invoked in SerialPort

Carsten Kanstrup

OmarMallat

Sorry about the confusions I created about BeginInvoke. You used it all right and I hope you have not changed the code to anything less optimized (you can show us the new code for a check). I just overlooked the small {bc} after New object() and thought that you used an empty object as you often see and therefore emtied the receive buffer from the UI thread. It also confused me a little with the combined AddData routine. Because the handler for the DataReceived event is called from a thread pool thread, you know for sure that invoke is always required when using a method on the UI thread, so why this combined method

Next time I will spend a little more time going through the code before answering.





Re: .NET Base Class Library DataReceived not Invoked in SerialPort

nobugz

You'd better post your question at one of the database forums on this web site. Don't mention the serial port stuff, just your problem with AddData().





Re: .NET Base Class Library DataReceived not Invoked in SerialPort

Carsten Kanstrup

I must admit that I don't find the thread exit message normal and would not allow it in one of my programs. It looks as your serial port closes or goes into timeout while the event handler is still waiting for data (ReadLine). It is probably the thread pool thread used for the event handler, which is aborted. There may be more reasons for that.

  • You do not close the port anywhere in your program so the garbage collector does it for you when it thinks that you don't use the port anymore (I mentioned that possibility previously).
  • Your device sends one or more character(s) after the NewLine character, which is used to terminate the telegram (ReadLine). If it does, DataReceived will fire once more and invoke your event handler again, but because you don't get any further NewLine character it may then go into timeout. Perhaps you should consider putting ReadLine into a try statement





Re: .NET Base Class Library DataReceived not Invoked in SerialPort

OmarMallat

nobugz, thank you for you answer, and about database, I will post new thread in its related forum.

Carsten.. for sure I close the port. but you give me a hint in your second point, because the barcode scanner may send another information like suffix and prefix and timeout reading and something like that... I will check with scanner support to see that and how to avoid it. I like your idea when you said that you refuse a same exit message in your program. nobody like that something happen out of sight.

ok, let me explain more about my program. I have 2 barcode scanners (Symbol MS-2204) fixed to read all cartons that pass in a production line in an industry. we use 2 scanner to be sure that our result is correct. each one is connected to a serial port

when I post my code, I post the code related to my problem only. so you couldn't find a port close command.

when I start using the serial port event, I try to use inside the event: Label1.Text = c.ReadLine() but it fails because of thread causes. I search the forum and I found that I should use delegates because the event fire on another thread.

Now I change the code because I found it more flexible to use 2 serial ports for each scanner. and in this new code, I didn't use delegate or threads or beginInvoke or invoke.... I can change the label and modify data directly

I create a class clsSerial.vb to open the serial port and raise a class event when the serial port receive data.:

Public Class clsSerial

Private _portNumber As Integer

Private WithEvents sp As System.IO.Ports.SerialPort

Public Event Data_Received(ByVal value As String)

Sub New(ByVal ComPortNumber As Integer)

_portNumber = ComPortNumber

End Sub

Public Sub Open()

sp = New System.IO.Ports.SerialPort("COM" & _portNumber, 9600, IO.Ports.Parity.None, 8, IO.Ports.StopBits.One)

sp.Open()

sp.DiscardInBuffer()

sp.DtrEnable = True

sp.RtsEnable = True

End Sub

Public Sub Close()

sp.Close()

End Sub

Private Sub sp_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles sp.DataReceived

Dim s As String = ""

Try

s = sp.ReadLine

RaiseEvent Data_Received(s)

Catch ex As Exception

MsgBox(ex.Message, MsgBoxStyle.Critical, "Error in sp_DataReceived ")

End Try

End Sub

Public ReadOnly Property IsOpen() As Boolean

Get

Return sp.IsOpen

End Get

End Property

End Class

and the main form contains status bar and a button to connect and disconnect and a grid to count items after each barcode read

Public Class frmMain

Private Enum eColIndex

Com1Col = 3

Com2Col = 4

End Enum

Private WithEvents Serial1 As clsSerial

Private WithEvents Serial2 As clsSerial

Private isReady As Boolean = False

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

'TODO: This line of code loads data into the 'DsCounter.Barcode' table. You can move, or remove it, as needed.

Me.BarcodeTableAdapter.Fill(Me.DsCounter.Barcode)

dgvBarcode.AutoResizeColumns()

End Sub

Private Sub btnConnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnConnect.Click

If btnConnect.Text = "Connect" Then

Try

Serial1 = New clsSerial(1)

Serial1.Open()

Serial2 = New clsSerial(2)

Serial2.Open()

isReady = True

btnConnect.Text = "Disconnect"

Catch ex As Exception

If Serial1.IsOpen Then Serial1.Close()

If Serial2.IsOpen Then Serial2.Close()

MsgBox(ex.Message)

End Try

Else

Serial1.Close()

Serial2.Close()

btnConnect.Text = "Connect"

End If

End Sub

Private Sub Serial1_Data_Received(ByVal value As String) Handles Serial1.Data_Received

lblBarcode1.Text = value

Dim s As String = ""

' the scanner send prefix and suffix data so I need to get only the barcode which is, in our case, digits only.

Try

For i As Integer = 0 To value.Length - 1

If IsNumeric(value.Substring(i, 1)) Then

s += value.Substring(i, 1)

End If

Next

Catch ex As Exception

MsgBox(ex.Message, MsgBoxStyle.Critical, "Error in Serial1 DataReceived ")

End Try

AddData(s, eColIndex.Com1Col)

End Sub

Private Sub Serial2_Data_Received(ByVal value As String) Handles Serial2.Data_Received

lblBarcode2.Text = value

Dim s As String = ""

Try

' the scanner send prefix and suffix data so I need to get only the barcode which is, in our case, digits only.

For i As Integer = 0 To value.Length - 1

If IsNumeric(value.Substring(i, 1)) Then

s += value.Substring(i, 1)

End If

Next

Catch ex As Exception

MsgBox(ex.Message, MsgBoxStyle.Critical, "Error in Serial1 DataReceived ")

End Try

AddData(s, eColIndex.Com2Col)

End Sub

Private Sub AddData(ByVal bc As String, ByVal colIndex As eColIndex)

Try

Select Case colIndex

Case eColIndex.Com1Col

ssLblBarcode1.Text = bc

Case eColIndex.Com2Col

ssLblBarcode2.Text = bc

End Select

Catch ex As Exception

MsgBox(ex.Message, MsgBoxStyle.Critical, "Error in changing Label ")

End Try

Dim r As dsCounter.BarcodeRow

r = DsCounter.Barcode.FindByBarcodeNum(bc)

If r Is Nothing Then

Try

r = DsCounter.Barcode.NewBarcodeRow

r.BarcodeNum = bc

r.Item(colIndex) = 1

Select Case colIndex

Case eColIndex.Com1Col

r.Item(eColIndex.Com2Col) = 0

Case eColIndex.Com2Col

r.Item(eColIndex.Com1Col) = 0

End Select

DsCounter.Barcode.Rows.Add(r)

Catch ex As Exception

MsgBox(ex.Message, MsgBoxStyle.Critical, "Error in Adding row ")

End Try

Else

Try

For i As Integer = 0 To dgvBarcode.Rows.Count - 1

If dgvBarcode.Item(0, i).Value = bc Then

dgvBarcode.Item(colIndex, i).Value += 1

Exit For

End If

Next

Catch ex As Exception

MsgBox(ex.Message, MsgBoxStyle.Critical, "Error in Editing Grid ")

End Try

End If

End Sub

Private Sub btnSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSave.Click

Try

Me.Validate()

Me.BarcodeBindingSource.EndEdit()

Me.BarcodeTableAdapter.Update(Me.DsCounter.Barcode)

ssLabel.Text = "All Changes was saved to the Database"

Catch ex As Exception

MsgBox(ex.Message)

End Try

End Sub

Private Sub btnReset_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnReset.Click

Dim r As dsCounter.BarcodeRow

For Each r In DsCounter.Barcode.Rows

r.BarcodeCount1 = 0

r.BarcodeCount2 = 0

Next

DsCounter.AcceptChanges()

End Sub

Private Sub dgvBarcode_CellValueChanged(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles dgvBarcode.CellValueChanged

If isReady Then

dgvBarcode.Rows.Item(e.RowIndex).DefaultCellStyle.ForeColor = Color.Red

End If

End Sub

End Class






Re: .NET Base Class Library DataReceived not Invoked in SerialPort

OmarMallat

I really like to know if creating a class is better or using delegate is better ( as logic)




Re: .NET Base Class Library DataReceived not Invoked in SerialPort

Carsten Kanstrup

You are actually using a delegate because events are nothing but delegates. Behind the scenes,

Public Event Data_Received(ByVal value As String)

is actually replaced with this delegate definition:

Public Delegate Sub Data_Received(ByVal value As String)

The compiler also creates add and remove methods for the delegate to facilitate dynamic event handling (AddHandler, RemoveHandler). The Event delegate always points to a subroutine because you cannot transfer the result of a function to the event source. Events and delegates are so closely related that you can set an event equal to a delegate like this:

Public Delegate Sub MyDelegate(ByVal value As String)

Public Event MyEvent As MyDelegate

This may be practical if you want to raise many events of the same type.

If there is one or more handlers for the event, the compiler will declare one or more new delegate(s) and set the address field(s) to point to the handler(s) (New Data_Received(AddressOf YourMethod), value) in the same way as you do in a control.BeginInvoke statement.

When you raise an event with:

RaiseEvent Data_Received(s)

it is - according to what I have been able to find so far - replaced with HandlerDelegate.Invoke. However this cannot be true. A delegate.Invoke does not return before the called method returns and it uses a thread pool thread, and this is probably not what happens. I presume that RaiseEvent will just put the handler delegates on the message queue as control.BeginInvoke does, but for the moment I am not sure about this. If my guess is correct, there is in practice no difference between raising an event and using control.BeginInvoke - the code is almost identical, so you cannot tell which method is best.

Personally, I have newer liked control.BeginInvoke. I find it confusing and not very intuitive and it do not utilize the flexibility of the three compiler generated methods - Invoke, BeginInvoke and EndInvoke - of the delegate. On the other hand, RaiseEvent is similar to all other events on the UI thread, so maybe this is the cleanest solution, but until I know for sure what goes on, I have not started to use this method myself. If it uses a thread pool thread for Invoke as indicated in the literature, the overhead is bigger than using control.beginInvoke.

EDIT

See this new thread, which covers the subject RaiseEvent contra BeginInvoke:

http://forums.microsoft.com/MSDN/ShowPost.aspx PostID=1726369&SiteID=1.

You better use control.BeginInvoke. Raising events the way you do is not thread safe and this may be the source of your problems.