This does not answer your specific question, but anyway...
More than a year ago, I implemented barcode reader support under even more adverse circumstances. It was for a reporting applicaton with association to logistical data in pure Java (cross platform rich client, primarily on Windows).
I found out the same you're saying about the keyboard driver, which prevents distinction of actual USB devices in user mode, at least at the first glance. There are more expensive devices with own drivers and advanced features, which would allow some sort of distinction.
All barcode readers I encountered in that environment were visible as keyboards and used to simply fill an SAP form field and hit the enter key, which is a common case. The termination may be configurable using 'magic barcodes' or another manufacturer specific method.
So the decision was against any JNI based, platform specific implementation.
Instead, I implemented also an interception-like approach (extended version of yours) by evaluating generic keyoard input within certain Swing/AWT forms using these criteria:
- key stroke frequency determined by the first two chars (initially / after timeout)
- jitter (frequency/rate change)
- set of valid chars
- terminating line break.
The input gets consumed by a buffer until the criteria for machine generated input aren't met, or the validation has been passed, where barcode listeners will be notified. In either situation, the input can be forwarded as if nothing else happened.
This has proven to be very accurate, since for a human, it's all but impossible to enter a valid sequence at the barcode reader's rate with (almost) zero jitter.
EDIT:
Just dug out the Java source; I can give you the code of an early revision of the implementation above as an example (no warranty, also consider to implement CR):
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A {@link KeyListener} implementation for barcode readers. This implementation
* checks for input rate and jitter to distinguish human and scanner
* input sequences by 'precision'. A barcode input sequence from a scanner is
* typically terminated with a line break.
*
* @author Me
*/
public abstract class AbstractBarcodeInputListener implements KeyListener {
public static final int DEFAULT_MIN_PAUSE = 300;// [ms]
public static final int DEFAULT_MAX_TIME_DELTA = 200;// [ms]
public static final int DEFAULT_MAX_TIME_JITTER = 50;// [ms]
public static Integer parseInt(Pattern pattern, int group, String line) {
final Matcher matcher = pattern.matcher(line);
if (matcher.matches())
return Integer.parseInt(matcher.group(group));
return null;
}
private String input;
private final long minPause;
private long maxTimeDelta;
private final long maxTimeJitter;
private long firstTime;
private long firstTimeDelta;
private long lastTimeDelta;
private long lastTime;
public AbstractBarcodeInputListener(long maxTimeDelta, long maxTimeJitter) {
this.input = new String();
this.minPause = AbstractBarcodeInputListener.DEFAULT_MIN_PAUSE;
this.maxTimeDelta = maxTimeDelta;
this.maxTimeJitter = maxTimeJitter;
this.firstTime = 0;
this.firstTimeDelta = 0;
this.lastTimeDelta = 0;
this.lastTime = 0;
}
public AbstractBarcodeInputListener() {
this(AbstractBarcodeInputListener.DEFAULT_MAX_TIME_DELTA,
AbstractBarcodeInputListener.DEFAULT_MAX_TIME_JITTER);
}
private boolean checkTiming(KeyEvent e) {
final int inputLength = this.input.length();
final long time = e.getWhen();
long timeDelta = time - this.lastTime;
long absJitter = 0;
long relJitter = 0;
boolean inputOK = true;
switch (inputLength) {
case 0: // pause check
inputOK &= (timeDelta > this.minPause);
this.firstTime = time;
this.firstTimeDelta = timeDelta = 0;
break;
case 1: // delta check
this.firstTimeDelta = timeDelta;
inputOK &= (timeDelta < this.maxTimeDelta);
break;
default:// jitter check & delta check
absJitter = Math.abs(timeDelta - this.firstTimeDelta);
relJitter = Math.abs(timeDelta - this.lastTimeDelta);
inputOK &= (absJitter < this.maxTimeJitter);
inputOK &= (relJitter < this.maxTimeJitter);
inputOK &= (timeDelta < this.maxTimeDelta);
break;
}
this.lastTime = time;
this.lastTimeDelta = timeDelta;
return inputOK;
}
@Override
public void keyPressed(KeyEvent e) {
}
private void clearInput() {
this.input = new String();
}
private void commitInput(KeyEvent e) {
final String code = this.input;
if (!code.isEmpty()) {
final long avgIntervalTime = e.getWhen() - this.firstTime;
this.maxTimeDelta = (avgIntervalTime * 15) / 10;
this.clearInput();
this.codeRead(code);
}
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
if (this.checkTiming(e)) {
final char c = e.getKeyChar();
switch (c) {
case '\b':
this.clearInput();
break;
case '\n':
this.commitInput(e);
break;
default:
this.input += c;
break;
}
} else {
this.clearInput();
}
}
public abstract void codeRead(String line);
}
Best Answer
It seems that your scanner is set to the wrong mode, which adds those suffixes to the bar codes.
For instance, you can program the Honeywell Genesis 7580g scanner in such a way, that it will add different suffixes to the barcodes, depending on the symbology. It's up to you, to add or not to add. But. You can set up the scanner interface so that it will add that suffixes no matter whether you want it or not.
That's what the "USB IBM SurePos" interface of the Genesis 7580g scanner will do. (AKA "USB Handheld Scanner -
PAPSPH
" command and "USB Tabletop Scanner -PAPSPT
" command)For this interface, the User Guide for my scanner states, that
and so on for Code 39, Code 128 and Interleaved 2 of 5 symbologies. And it looks just like your case.
All you need if you had my scanner is to program just "USB HID" interface (command
PAP131
) instead of "USB IBM SurePos" interface (commandsPAPSPH
orPAPSPT
).