Java中的条形码扫描器实现

31

尊敬的先生们,我有一个问题。我目前正在进行学校的Java项目,需要将USB条形码扫描器作为外部输入连接到我的笔记本电脑上。由于学生来说它相当昂贵,我实际上还没有购买USB扫描仪。因此,我必须收集证据证明该扫描仪可以与我的程序配合使用。

扫描仪能否从条形码中读取并将其存储到变量中(可能是在线打印的)?如果可以,按下扫描器的动作事件是否会像键盘按键一样被读取?如果是,那么代码行应该长什么样子?

另外,如果您能发布有关条形码扫描仪的经验或给出任何建议,例如要购买哪种扫描仪,那将非常有帮助。谢谢!


3
如果您的光标在文本框或输入字段中,扫描仪将只是在那里“写入”代码。扫描仪没有特殊之处,您将不会收到事件。但是,如果您有一个网页,可以尝试使用JS的“OnChange”事件。 - Nishant
4个回答

34

我最近需要实现一个与Java交互的扫描器系统。

我使用了Honeywell Voyager MS9540 USB条形码扫描器。

默认情况下,扫描器会将数据直接发送为键盘输入 - 无需驱动程序。

但是很容易让这款扫描仪与Java 直接交互,而不是使用键盘挂钩(像您提到的那样,将条形码用作Java中的变量)。

此型号具有模拟串行端口的设置,然后您可以使用javax.comm软件包读取扫描的数据。对我来说,这比使用键盘挂钩获取条形码数据要好得多,因为程序在能够解释扫描之前不需要焦点(我不想创建全局键盘挂钩)。

我的Java程序从指定的串行端口读取所有输入,并将条形码写入数据库。我还设置了程序以将任何未识别的条形码扫描传递给键盘(我没有创建的任何应用程序上的条码 - 我在我的条码上使用了独特的签名),以便它可以作为常规条形码扫描仪适用于任何其他可能从键盘读取条形码的应用程序。

您可能可以通过进行一些密集的JNI编码(而不是使用此型号所具有的串行端口仿真)直接从任何USB扫描仪读取数据,但我没有准备花时间去研究本地代码。

要为串行端口仿真配置特定型号,您只需使用您要配置的扫描仪在此文档中扫描特定条形码。它的名称为“串行仿真模式”的条形码。

此扫描仪需要驱动程序才能进行串行端口仿真。我在这里找到了实现说明和所需驱动程序(在“软件”选项卡下)。下载名为“Honeywell Scanning and Mobility(HSM)USB Serial Driver”的软件包。标题为“HSM USB Serial Driver Getting Started Guide”的PDF文件中有说明。

如果您不熟悉javax.comm API,请阅读Rick Proctor在此处的示例中的介绍,它会告诉您在哪里获取jar文件以及要将文件放在何处(javax.comm不是大多数Java软件包的标准组件)。

我相信还有其他具有串行端口仿真功能的扫描器型号(我不为Honeywell工作)。

这是我条形码阅读器类的一个精简版本:

package scanhandler;


import java.awt.AWTException;
import java.awt.Robot;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Enumeration;
import java.util.Properties;
import java.util.TooManyListenersException;
import javax.comm.CommPortIdentifier;
import javax.comm.PortInUseException;
import javax.comm.SerialPort;
import javax.comm.SerialPortEvent;
import javax.comm.SerialPortEventListener;
import javax.comm.UnsupportedCommOperationException;

public class ScanHandler implements Runnable, SerialPortEventListener {

    private static CommPortIdentifier   myCommPortIdentifier;
    private static Enumeration          portList;
    private static String               TimeStamp;
    private static String               driverClass;
    private static String               connectionString;
    private static String               comPort;    

    private Connection                  myConnection;
    private InputStream                 myInputStream;
    private Robot                       myRobot;
    private SerialPort                  mySerialPort;
    private Thread                      myThread;


    public ScanHandler() {

        // open serial port
        try {
            TimeStamp = new java.util.Date().toString();
            mySerialPort = (SerialPort) myCommPortIdentifier.open("ComControl", 2000);
            //System.out.println(TimeStamp + ": " + myCommPortIdentifier.getName() + " opened for scanner input");
        } catch (PortInUseException e) {
            e.printStackTrace();
        }

        // get serial input stream
        try {
            myInputStream = mySerialPort.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // add an event listener on the port
        try {
            mySerialPort.addEventListener(this);
        } catch (TooManyListenersException e) {
            e.printStackTrace();
        }
        mySerialPort.notifyOnDataAvailable(true);

        // set up the serial port properties
        try {
            mySerialPort.setSerialPortParams(9600,
                SerialPort.DATABITS_8,
                SerialPort.STOPBITS_1,
                SerialPort.PARITY_NONE);
            mySerialPort.setDTR(false);
            mySerialPort.setRTS(false);

        } catch (UnsupportedCommOperationException e) {
            e.printStackTrace();
        }

        // make a robot to pass keyboard data
        try {
            myRobot = new Robot();
        } catch (AWTException e) {
            e.printStackTrace();
        }

        // create the thread
        myThread = new Thread(this);
        myThread.start();
    }

    public void run() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {}
    }

    // on scan
    public void serialEvent(SerialPortEvent event) {

        if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE) {

            StringBuilder myStringBuilder = new StringBuilder();
            int c;
            try {

                // append the scanned data onto a string builder
                while ((c = myInputStream.read()) != 10){
                   if (c != 13)  myStringBuilder.append((char) c);
                }               

                // send to keyboard buffer if it the barcode doesn't start with '5'
                if (myStringBuilder.charAt(0) != '5') {

                    for (int i = 0; i < myStringBuilder.length(); i++) {
                        myRobot.keyPress((int) myStringBuilder.charAt(i));
                        myRobot.keyRelease((int) myStringBuilder.charAt(i));
                    }

                // here's the scanned barcode as a variable!
                } else {
                    TimeStamp = new java.util.Date().toString();
                    System.out.println(TimeStamp + ": scanned input received:" + myStringBuilder.toString());                    
                }

                // close the input stream
                myInputStream.close();

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {

        // read ScanHandler properties
        Properties myProperties = new Properties();
        try {
            myProperties.load(new FileInputStream("config.properties"));
            comPort             = myProperties.getProperty("ScanHandler.comPort");
        } catch (IOException e) {
            e.printStackTrace();
        }              

        try {

            // get our pre-defined COM port
            myCommPortIdentifier = CommPortIdentifier.getPortIdentifier(comPort);
            ScanHandler reader = new ScanHandler();

        } catch (Exception e) {
            TimeStamp = new java.util.Date().toString();
            System.out.println(TimeStamp + ": " + comPort + " " + myCommPortIdentifier);
            System.out.println(TimeStamp + ": msg1 - " + e);
        }
    };    
}

看起来这些驱动程序只适用于Windows,因此如果您以这种方式进行操作,您将无法在除Windows之外的其他操作系统上使用您的程序。 - jobukkit
可能是这样,我只需要在Windows上使用它。我还没有深入搜索,但应该有一些可以将USB转换为Linux或MAC的COM端口的东西。 - egerardus
@Geronimo:我应该从哪里找到这个config.properties文件? - Farhan stands with Palestine
@ShirgillAnsari 我认为示例代码仅从config.properties文件中读取com端口,例如:"COM1"、"COM2"、"COM3"等,无论你的条码扫描器连接到哪个串行端口。 - egerardus
@Denys,谢谢,JNI。当我写这个的时候一定是在做一些Tomcat配置... - egerardus
显示剩余4条评论

12

我使用过的条形码扫描器表现得像一个键盘设备(在操作系统中显示为HID键盘USB设备)。当扫描条形码时,它会将代码发送给电脑,就好像是手动输入的一样,不需要使用特殊的API与其进行交互。


我可以问一下,你买了哪个型号的? - user976123
@user976123 这是几年前在以前的工作中发生的事情,很抱歉我不记得那个型号是什么了。 - prunge

8

虽然这是一个相当古老的帖子,但是搜索引擎可以帮助您找到它。

这可以作为对Geronimo答案的补充:

对于Linux操作系统,串行仿真模式下的条码扫描仪无需安装驱动程序,因为USB串行端口具有本地支持。我们使用几种类型的Honeywell扫描仪,它们全部开箱即用,这些串行仿真扫描仪在我们的系统中显示为/dev/ttyACM0、/dev/ttyACM1等。

最近,我们已经从javax.comm转向jssc作为Java库来接口串行端口。如果我没记错的话,在Windows 7 64位系统下,javax.comm库不能读取或写入串行端口,而jssc具有非常相似的API。


欢迎来到stackoverflow。请尝试使用编辑器格式化您的答案。 - Nagama Inamdar

0

我知道这是一个老问题,但我想为模拟条形码扫描器输入添加另一种解决方案。此解决方案仅适用于将扫描器输入模拟为键盘数据。

由于扫描器通常只使用键盘输入,因此我们可以使用AutoHotkey脚本来模拟此过程。以下是一个示例脚本:

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.

; Barcode 1
; Hotkey = ctrl + alt + 1
^!1::
    SendInput [BC200015]
Return

; Barcode 2
; Hotkey = ctrl + alt + 2
^!2::
    SendInput [BC300013]
Return

只需将[BC300013][BC200015]替换为您期望的扫描器输入即可。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接