Reading USB HID barcode scanner input without knowing VID&PID

后端 未结 1 1550
忘掉有多难
忘掉有多难 2020-12-16 03:12

I\'m trying to develop device independent library for barcode scanners, it has to be working in windows environment.

I\'ve done some research in this field, afaik m

相关标签:
1条回答
  • 2020-12-16 03:32

    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);
    }
    
    0 讨论(0)
提交回复
热议问题