Coding the Application
With the UI of the application out of the way, you can now focus on writing the code to wire up all the controls. Switch to the code-behind of Form1 and import the following namespaces.
Imports System.Data
Imports System.Data.SqlClient
Imports System.IO
Declare the member variables and constants shown below:
Public Class Form1
'---serial port to listen to
' incoming data---
Private WithEvents serialPort As New IO.Ports.SerialPort
'---tag ID read from the reader---
Private tagID As String = String.Empty
'---the time that the tag ID was recorded---
Private timeRecorded As DateTime = Now
'---COM port to listen to---
Const COM As String = "COM3"
'---file name of the log file---
Const FILE_NAME As String = "C:\Attendance.csv"
'---the interval before the employee record is cleared
' from the screen (in seconds)---
Const INTERVAL As Integer = 3
When the form loads, you first clear the displayed employee by setting its filter to a non-existent tag ID. The Timer control clears the displayed employee after a certain amount of time, and in this case you will set it to three seconds (as defined by the Interval constant). That is to say, when an employee is identified using his RFID tag, his information will be cleared from the screen after three seconds.
As the Parallax's RFID Reader Module uses a serial connection, you will use the SerialPort class to communicate with the reader.
Author's Note: For this example, I have assumed that you'll connect the COM3 port to the RFID Reader Module. You need to change it to the correct port number for your own use. |
Here's the code for the
Form1_Load event.
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 NorthwindDataSet.
' Employees' table. You can move, or remove it, as needed.
Me.EmployeesTableAdapter.Fill(Me.NorthwindDataSet.Employees)
'---Clear the employee when the app is loaded
EmployeesBindingSource.Filter = "TAGID='xxxxxxxxxx'"
'---set the timer interval to clear the employee record
' convert to milliseconds
Timer1.Interval = INTERVAL * 1000
'---open the serial port connecting to the reader
If serialPort.IsOpen Then
serialPort.Close()
End If
Try
With serialPort
.PortName = COM
.BaudRate = 2400
.Parity = IO.Ports.Parity.None
.DataBits = 8
.StopBits = IO.Ports.StopBits.One
.Handshake = IO.Ports.Handshake.None
End With
serialPort.Open()
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
To receive incoming data from the SerialPort class, you need to service the
DataReceived event. In this case, when incoming data is received, you will update the
txtTagID control. Here's the code for the
DataReceived event.
Private Sub DataReceived(ByVal sender As Object, _
ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _
Handles serialPort.DataReceived
'---when incoming data is received, update the TagID
' textbox
txtTagID.BeginInvoke(New myDelegate( _
AddressOf updateTextBox), New Object() {})
End Sub
You need to define a delegate to call a routine to update the
txtTagID control. Here, define the
myDelegate() delegate and the
updateTextBox() subroutine.
'--- update the Tag ID textbox
Public Delegate Sub myDelegate()
Public Sub updateTextBox()
' for receiving plain ASCII text
With txtTagID
.AppendText(serialPort.ReadExisting)
.ScrollToCaret()
End With
End Sub
One important point you need to understand about RFID readers (at least for the two RFID readers shown in this article) is that while it's scanning a tag, it will continuously send the tag ID to the serial connection. For example, suppose a tag with ID of
0F0296AF3C is placed near the reader. In this case, the reader will continuously send the value of
0F0296AF3C to the serial connection. For the Parallax's reader, each value starts with the line feed character (character 10) and ends with a carriage return character (character 13). To make matters complicated, using the
ReadExisting() method of the SerialPort class does not guarantee that you will read the tag ID in its entirety. This is because a value may be sent in four blocks, like this:
<10>0F
029
6AF3
C<13>
You may be tempted to use the
ReadLine() method of the SerialPort class to read incoming data, but that will not work, because the
ReadLine() method looks for
<13><10> at the end of the line. But because the incoming data does not end with
<10>, this will cause the application to go into an infinite loop.
In addition, if you don't clear the incoming data buffer fast enough, you may get a series of data queued up like this:
<10>
0F
029
6AF3
C
<13>
<10>
04
158D
C82B
<13>
Instead of writing elaborate logic to process the incoming data, an easy way is to append all incoming data to a TextBox control (with the
Multiline property set to
True). Using the data just described,
Figure 13 shows what the TextBox control will look like.
 | |
Figure 13: Appending incoming data to a TextBox control. |
|
 | |
Figure 14: The state of the TextBox control containing the last incomplete tag ID. |
|
|
The second to the last line will hence always contain the tag ID that you are interested in if the last line is an empty string. In contrast, if the tag ID is only partially received, the state of the TextBox control would be as shown in
Figure 14.
As all incoming data is updated in the TextBox control, you can check if the tag ID belongs to an employee whenever there are changes in the content of the TextBox control. You'll use the
TextChanged event (
Listing 1) to detect the change.
The
TextChanged event first examines if the last line in the
txtTagID control is an empty string; if it is, then the scanned tag ID can be found in the second to last line. Using this tag ID, the code checks the time difference between the current time and the last time the tag ID was read. If it is less than three seconds and the tag ID is the same as the last read tag ID, it means that it is reading the same user, so the current tag ID should be ignored. Using this implementation, all users will be ignored for three seconds following an initial tag scan.
Using the tag ID, apply a filter to the EmployeesBindingSource control to look for an employee with a matching tag ID. If an employee is found, an entry will be written to the log file using the
WriteToLog() subroutine.
 | |
Figure 15: The content of a typical log file. |
Private Sub WriteToLog( _
ByVal employeeID As String, _
ByVal employeeName As String)
'--- write to log file
Dim str As String = employeeID & _
"," & employeeName & ", " & _
Now & Chr(13)
My.Computer.FileSystem. _
WriteAllText(FILE_NAME, str, True)
End Sub
Figure 15 shows the content of a typical log file.
The Timer control will fire the
Tick event every three seconds (as determined by the value set in its
Interval property). Hence you need to service the
Tick event so that every time it fires, you can clear the current employee information that is displayed. Here is the implementation of the
Tick event.
Private Sub Timer1_Tick(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Timer1.Tick
'--- clear the employee
EmployeesBindingSource.Filter = "TAGID='xxxxxxxxxx'"
Timer1.Enabled = False
End Sub
If the tag ID just scanned in does not belong to any user, the administrator can assign the tag ID to an employee. To do so, he can first click the Find button to find the user and then assign the tag ID to that user. Here is the implementation of the Find button.
Private Sub btnFind_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnFind.Click
'--- search for employee
If txtEmployeeID.Text = String.Empty Then
EmployeesBindingSource.RemoveFilter()
Else
EmployeesBindingSource.Filter = _
"EmployeeID='" & txtEmployeeID.Text & "'"
End If
End Sub
Basically, you search for a user by applying a filter to the EmployeesBindingSource control. To assign a tag ID to the current employee, copy the tag ID onto the
TagIDLabel1 control and then save the changes.
Listing 2 shows the implementation of the "Assign Tag to Employee" button.
The "Deassign Tag From Employee" button allows a tag ID to be disassociated from an employee. This is easily accomplished by setting the TagIDLabel1 control to an empty string. Here's the implementation of the "Deassign Tag From Employee" button.
Private Sub btnDeassign_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles btnDeassign.Click
If Trim(TagIDLabel1.Text) = String.Empty Then
ToolStripStatusLabel1.Text = _
"Current employee has no tag ID."
Exit Sub
End If
'---deassociate tag ID from employee
TagIDLabel1.Text = String.Empty
'---save the record
Me.Validate()
Me.EmployeesBindingSource.EndEdit()
Me.EmployeesTableAdapter.Update( _
Me.NorthwindDataSet.Employees)
ToolStripStatusLabel1.Text = _
"Tag deassociated from employee."
End Sub