[SOLVED] Best way to automatically read an RFID input to a text field

I have a system where the user swipes their badge on an RFID reader to login to the computer.
This is done with a screen, a text field, and the PropertyChange event. If the RFID input is ‘ABC’ then I trigger the PropertyChange event at least 3x. Once when ‘A’, once when ‘AB’, and once when ‘ABC’. I thought the system function ‘InvokeLater()’ might limit this but it seems to just delay all 3x propery change events. Given that the RFID might be a variable length what is the best way to go about triggering some script here without calling PropertyChange several times?

import time
def evaluateRFID():
    fakeRFID = event.source.text
    lenRFID = len(event.source.text)
    padRFID = 10 - lenRFID

    padZERO = ''
    for x in range(padRFID):
        padZERO = padZERO + '0'
    trueRFID = "BD" + padZERO + fakeRFID
    if len(trueRFID)==12:
        if event.propertyName == 'text':
            if valid:
                #do some stuff
                #do some other stuff
system.util.invokeLater(evaluateRFID, 2500)

Does the text field have the Defer Updates property set? What keystrokes is the RFID reader sending, exactly? Generally speaking, this should be solved for you by the combination of those two - typically, barcode or RFID readers send the scanned input, then some ending delimiter (usually, an Enter keypress), which would then cause a single property change event.

Your idea with invokeLater could work - but you’d have to use the timing as a ‘debounce’ - so each new invokeLater that’s created would have to check if another invokeLater task had been created since it was, and decide not to run, until the final invokeLater times out and actually runs. This is pretty tricky to reason about, so I’d recommend playing with Defer Updates and the scanner’s settings, if at all possible, to avoid the headache.

I don’t see a ‘defer updates’ property available with the text field. The key strokes are sending ‘68452’ exactly. The customer doesn’t want a ‘Enter’ button. They just need to swipe their badge and some stuff happens around them.

So I took your advice in the second part and did a “lock” type deal on the invokeLater using a memory tag.

invokeLaterStatus = system.tag.read("[default]Base/Memory/invokeLater").value
if invokeLaterStatus == False:
    system.tag.write("[default]Base/Memory/invokeLater", True)					
    system.util.invokeLater(evaluateRFID, 2500)

Maybe not ideal but better than nothing?

If you are using a tag as a lock for client-side operations, you really should use a client-tag. Otherwise two clients swiping at the same time will clash. Also, consider using a timestamp as your lock tag, setting it just before invokeLater, and comparing inside the invokeLater . Skip if it has changed further.

The client-tag makes sense to me but I don’t understand the timestamp.

A millisecond timestamp captured in the event, then both saved in the client tag and passed to the deferred function, will let the deferred function identify if it is the last one. If not last, the client tag would be overwritten with a newer timestamp by the time the function runs. Therefore, if saved timestamp doesn’t match the timestamp passed to the function, exit without doing anything.

If you want to be really particular, use a long from java’s System.nanoTime() instead of a millisecond timestamp.

If the tag reads are always five characters, you can also look at the length of the string to see if it needs processing.

Ideally, the reader should send some sort of delimiter as a suffix after it finishes reading the tag.

Here is my final code… timestamps might be better but I’m not choosing that route today.

def evaluateRFID():
    user_Lookup = system.db.runPrepQuery("SELECT * FROM OperatorTable WHERE BadgeID = ?", [event.source.text])
    if len(user_Lookup) != 0:
        if str(event.source.text) == str(user_Lookup[0][0]):
            system.nav.closeWindow('Main Windows/Login')
            system.nav.openWindow('Main Windows/Overview')

    system.tag.write("[Client]Memory/invokeLater", False)

invokeLaterStatus = system.tag.read("[Client]Memory/invokeLater").value
if invokeLaterStatus == False: #check if function has already been invoked by a property change.
    system.tag.write("[Client]Memory/invokeLater", True)					
    system.util.invokeLater(evaluateRFID, 2500) #delay execution of function evaluateRFID for 2.5 seconds

We use the KeyReleased property to trigger reading of an RFID input.

if event.keyCode == 10:
Then your code

1 Like

This also worked correctly and is a much simpler setup in general. Thank you!