Last active
July 11, 2021 09:26
-
-
Save svvitale/20f4e7ecc30082ac27b9 to your computer and use it in GitHub Desktop.
C# class that reads RFID cards and inserts each new read into a local MongoDB store. Reads are then pushed to a RESTful web service in a background task.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel; | |
using System.Threading.Tasks; | |
using System.Windows.Forms; | |
using RFIDeas_pcProxAPI; | |
using System.Net; | |
using System.Threading; | |
using System.IO; | |
using Newtonsoft.Json; | |
namespace Node { | |
public partial class EventLog : Form { | |
// Local cache task list | |
private List<Task> activeTasks = new List<Task>(); | |
// Database polling task | |
private Task dbPollingTask; | |
// Cancellation Token | |
private CancellationTokenSource databasePollCancel = new CancellationTokenSource(); | |
// Event grid and last card tracking by reader ID | |
private BindingList<EventData> eventDataGrid = new BindingList<EventData>(); | |
private static Dictionary<short, String> lastCardIDbyDeviceID = new Dictionary<short, String>(); | |
// Tap metrics | |
private int tapsRecorded = 0; | |
private int tapsSentToWeb = 0; | |
private int tapsDuplicate = 0; | |
// Set of devices that was present during last polling cycle. | |
HashSet<short> lastPolledDevices = new HashSet<short>(); | |
public EventLog() { | |
InitializeComponent(); | |
loadConfig(); | |
// Only search for USB prox readers | |
pcProxDLLAPI.SetConnectProduct( pcProxDLLAPI.PRODUCT_PCPROX ); | |
pcProxDLLAPI.SetDevTypeSrch( pcProxDLLAPI.PRXDEVTYP_USB ); | |
// See what's out there | |
RefreshDevices(); | |
// Start the read timer. Hardware limits polling to no less than 250ms between reads. | |
readProxTimer.Start(); | |
dataGridView1.DataSource = eventDataGrid; | |
// If enabled, start polling our local database for items that need to be pushed to web services. | |
if ( chkWebEnabled.Checked ) { | |
startDatabasePolling(); | |
} | |
} | |
private void RefreshDevices() { | |
// Disconnect, reconnect, and update our visible device count. | |
pcProxDLLAPI.USBDisconnect(); | |
if ( pcProxDLLAPI.usbConnect() == 1 ) { | |
txtDeviceCount.Text = pcProxDLLAPI.GetDevCnt().ToString(); | |
} | |
else { | |
txtDeviceCount.Text = 0.ToString(); | |
} | |
} | |
private void EventLog_FormClosing( object sender, FormClosingEventArgs e ) { | |
// Shut down all active tasks gracefully. | |
readProxTimer.Stop(); | |
stopDatabasePolling(); | |
saveConfig(); | |
Task.WaitAll( activeTasks.ToArray() ); | |
} | |
private void loadConfig() { | |
txtUrl.Text = Properties.Settings.Default.WebServicesURL; | |
txtEventID.Text = Properties.Settings.Default.WebServicesEventID; | |
chkWebEnabled.Checked = Properties.Settings.Default.WebServicesEnabled; | |
} | |
private void saveConfig() { | |
Properties.Settings.Default.WebServicesURL = txtUrl.Text; | |
Properties.Settings.Default.WebServicesEventID = txtEventID.Text; | |
Properties.Settings.Default.WebServicesEnabled = chkWebEnabled.Checked; | |
Properties.Settings.Default.Save(); | |
} | |
private void startDatabasePolling() { | |
// Start a task that polls the database and posts to the web service | |
dbPollingTask = Task.Factory.StartNew( async () => // <- marked async | |
{ | |
while ( true ) { | |
foreach ( EventData data in Mongo.GetLocalEvents() ) { | |
try { | |
// Update event ID | |
data.EventID = txtEventID.Text; | |
// Serialize to JSON | |
string json = JsonConvert.SerializeObject( data ); | |
// HTTP POST to the web service | |
HttpWebRequest http = WebRequest.CreateHttp( txtUrl.Text ); | |
http.Accept = "application/json"; | |
http.ContentType = "application/json"; | |
http.Method = "POST"; | |
http.ContentLength = json.Length; | |
http.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"; | |
using ( var writer = new StreamWriter( http.GetRequestStream() ) ) { | |
writer.Write( json ); | |
writer.Close(); | |
} | |
HttpWebResponse response = null; | |
try { | |
response = (HttpWebResponse) http.GetResponse(); | |
// If we get this far, the web post went fine. Update the local database | |
data.SentToWeb = true; | |
} | |
catch ( WebException ex ) { | |
if ( ( (HttpWebResponse) ex.Response ).StatusCode == HttpStatusCode.Conflict ) { | |
// Succeeded, but the server has identified this record as a duplicate. | |
data.SentToWeb = true; | |
Interlocked.Increment( ref this.tapsDuplicate ); | |
} | |
} | |
finally { | |
if ( response != null ) { | |
response.Close(); | |
} | |
} | |
// Check if we need to update/save the local record. | |
if ( data.SentToWeb == true ) { | |
Mongo.UpdateEvent( data ); | |
Interlocked.Increment( ref this.tapsSentToWeb ); | |
} | |
} | |
catch { | |
// No matter what happens, keep trying to send. | |
} | |
} | |
await Task.Delay( 250, databasePollCancel.Token ); // <- await with cancellation | |
} | |
}, databasePollCancel.Token ); | |
} | |
private void stopDatabasePolling() { | |
if ( dbPollingTask != null ) { | |
// Cancel the Polling | |
databasePollCancel.Cancel(); | |
// Wait for the task to complete | |
try { | |
dbPollingTask.Wait(); | |
} | |
catch ( AggregateException ) { | |
// Task cancelled successfully | |
} | |
} | |
} | |
private void ReportPlugStatusChanges( ref HashSet<short> polledDevices ) { | |
// current devices - previous devices = new devices | |
HashSet<short> newDevices = new HashSet<short>( polledDevices ); | |
newDevices.ExceptWith( lastPolledDevices ); | |
foreach ( short newLUID in newDevices ) { | |
HipChat.SendInfo( String.Format( "Reader #{0} attached.", newLUID ) ); | |
} | |
// previous devices - current devices = removed devices | |
HashSet<short> removedDevices = new HashSet<short>( lastPolledDevices ); | |
removedDevices.ExceptWith( polledDevices ); | |
foreach ( short newLUID in removedDevices ) { | |
HipChat.SendError( String.Format( "Reader #{0} unplugged.", newLUID ) ); | |
} | |
// Done reporting, current data becomes previous data. | |
lastPolledDevices = polledDevices; | |
} | |
private void readProxTimer_Tick( object sender, EventArgs e ) { | |
readProxTimer.Stop(); | |
HashSet<short> polledDevices = new HashSet<short>(); | |
for ( short devIdx = 0; devIdx < pcProxDLLAPI.GetDevCnt(); devIdx++ ) { | |
if ( pcProxDLLAPI.SetActDev( devIdx ) != 0 ) { | |
// Device is valid, try to read a card | |
String newCardID = pcProxDLLAPI.getCardID(); | |
short readerID = pcProxDLLAPI.GetLUID(); | |
// Add this reader's LUID to our hash of polled devices. | |
polledDevices.Add( readerID ); | |
// Initialize the last card ID if it's not already present | |
if ( !lastCardIDbyDeviceID.ContainsKey( readerID ) ) { | |
lastCardIDbyDeviceID[readerID] = ""; | |
} | |
if ( newCardID != lastCardIDbyDeviceID[readerID] ) { | |
if ( newCardID != "" ) { | |
// Create the EventData object and add it to our log | |
EventData data = new EventData( newCardID, readerID ); | |
eventDataGrid.Add( data ); | |
// Only maintain the last 50 events | |
if ( eventDataGrid.Count > 50 ) { | |
eventDataGrid.RemoveAt( 0 ); | |
} | |
// Create a new task for the actual database write. i.e. do it in another thread. | |
activeTasks.Add( Task.Factory.StartNew( () => { | |
// Add to the local database | |
Mongo.AddEvent( data ); | |
Interlocked.Increment( ref tapsRecorded ); | |
} ) ); | |
} | |
// Update our saved card ID | |
lastCardIDbyDeviceID[readerID] = newCardID; | |
} | |
} | |
} | |
statusLabel.Text = String.Format( "Recorded {0} / {1} Sent / {2} Duplicates", this.tapsRecorded, this.tapsSentToWeb, this.tapsDuplicate ); | |
ReportPlugStatusChanges( ref polledDevices ); | |
RefreshDevices(); | |
readProxTimer.Start(); | |
} | |
private void chkWebEnabled_CheckedChanged( object sender, EventArgs e ) { | |
txtEventID.Enabled = txtUrl.Enabled = !chkWebEnabled.Checked; | |
if ( chkWebEnabled.Checked ) { | |
startDatabasePolling(); | |
} | |
else { | |
stopDatabasePolling(); | |
} | |
} | |
private void status_DoubleClick( object sender, EventArgs e ) { | |
// Clear metrics | |
Interlocked.Exchange( ref tapsSentToWeb, 0 ); | |
Interlocked.Exchange( ref tapsRecorded, 0 ); | |
Interlocked.Exchange( ref tapsDuplicate, 0 ); | |
} | |
} | |
} |
Hi Infiniium,
Did you managed to get this? Thanks!
Joie
Same here, how do you add the dll?
Ok, you don't add the dll "as reference" as usual in visual studio, and you need a wrapper for the api dll. Check this out https://github.com/imbenjamin/RadioTrackLineside/blob/master/RadioTrack_ReaderClient/RFIDeas_pcProxAPI.cs
String newCardID = pcProxDLLAPI.getCardID();
The wrapper did not define this, is there a different wrapper for this?
Mr. Vitale, can you post the RFIDeas_pcProxAPI.cs.
TIA
never mind, you must be having tight pockets
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi Scott,
May I ask how did you add the pcProxAPI dll to C#? I'm having trouble with that, it says:
A reference to 'pcProxAPI.dll' could not be added. Please make sure that the file is accessible, and that it is a valid assembly or COM component.
Thanks!