JAVA读取USB扫描枪

时间:2022-05-25 17:23:36

利用JAVA程序获取扫描枪的数据

package barcode;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
*
*条形码数据缓存区
* 扫描服务获取到扫描枪输入的数据后将数据加入此缓存区
* 消费者线程会从此缓冲区中获取数据并执行数据的保存操作
* 数据的保存可以有多种实现
* 此缓冲区的意义在于不要因为数据保存出错或速度慢而影响扫描服务工作
*
* @author ysc
*/
public class BarcodeBuffer {
//阻塞队列
private static BlockingQueue queue=new LinkedBlockingQueue();
/**
* 生产一条数据,此方法由BarcodeProducter调用
* @param barcode 
*/
public static void product(String barcode){
queue.add(barcode);
}
/**
* 消费一条数据,如果队列中没有数据,此方法阻塞等待数据的到来,此方法由BarcodeConsumer调用
* @return 
*/
public static String consume() throws InterruptedException{
return queue.take();
}
}
package barcode;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
/**
*此消费者线程会从此缓冲区中获取数据并执行数据的保存操作
* 数据的保存调用BarcodeSaveService接口定义的save方法
*
* @author ysc
*/
public class BarcodeConsumer {
//消费者线程
private Thread thread;
//数据保存服务(可有多个)
private List barcodeSaveServices=new ArrayList();
private boolean quit;
/**
* 停止消费者线程
* 此方法在tomcat关闭的时候被调用
*/
public void stopConsume(){
if(thread!=null){
thread.interrupt();
//释放资源
for(BarcodeSaveService barcodeSaveService : barcodeSaveServices){
barcodeSaveService.finish();
}
}
}
/**
* 启动消费者线程
* 此方法在tomcat启动的时候被调用
*/
public void startConsume(){
//防止重复启动
if(thread!=null && thread.isAlive()){
return;
}
System.out.println("条形码消费者线程启动");
System.out.println("注册条形码保存服务");
registerBarcodeSaveServcie();
thread=new Thread(){
@Override
public void run(){
while(!quit){
try{
//当缓冲区没有数据的时候,此方法会阻塞
String barcode=BarcodeBuffer.consume();
if(barcodeSaveServices.isEmpty()){
System.out.println("没有注册任何条形码保存服务");
}
for(BarcodeSaveService barcodeSaveService : barcodeSaveServices){
barcodeSaveService.save(barcode);
}
}catch(InterruptedException e){
quit=true;
}
}
System.out.println("条形码消费者线程退出");
}
};
thread.setName("consumer");
thread.start();
}
/**
* 消费者线程从缓冲区获取到数据后需要调用保存服务对数据进行处理
*/
private void registerBarcodeSaveServcie() {
List classes=getBarcodeSaveServcieImplClasses();
System.out.println("条形码保存服务实现数目有:"+classes.size());
for(String clazz : classes){
try{
BarcodeSaveService barcodeSaveService=(BarcodeSaveService)Class.forName(clazz).newInstance();
barcodeSaveServices.add(barcodeSaveService);
}catch(Exception e){
e.printStackTrace();
}
}
}
/**
* 从类路径下的barcode.save.services文件中获取保存服务类名
* @return 多个保存服务实现类名
*/
private List getBarcodeSaveServcieImplClasses() {
List result=new ArrayList();
BufferedReader reader = null;
InputStream in=null;
try {
//放在WEB-INF/classes下的"barcode.save.services"会覆盖jar包中的"barcode.save.services"
URL url=Thread.currentThread().getContextClassLoader().getResource("barcode.save.services");
System.out.println("url:"+url.getPath());
in=url.openStream();
if(in==null){
System.out.println("没有在类路径下找到条码保存服务配置文件:barcode.save.services");
return result;
}
reader = new BufferedReader(new InputStreamReader(in,"utf-8"));
String line=reader.readLine();
while(line!=null){
//忽略空行和以#号开始的注释行
if(!"".equals(line.trim()) && !line.trim().startsWith("#")){
result.add(line);
}
line=reader.readLine();
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
if(reader!=null){
reader.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
try {
if(in!=null){
in.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return result;
}
}
package barcode;
import java.util.HashMap;
import java.util.Map;/**
*扫码枪模拟的键盘按钮事件监听(0-9键和回车键)
* 关键算法:条形码扫描器在很短的时间内输入了至少 barcodeMinLength 个字符以上信息,并且以“回车”作为结束字符,并且一次扫描要在 maxScanTime 毫秒内完成
* 字符数及扫描时间可根据具体情况设置
* @author ysc
*/
public class BarcodeKeyboardListener{
//条形码数据缓充区
private StringBuilder barcode;
//扫描开始时间
private long start;
private Map keyToLetter=new HashMap();
//一次扫描的最长时间
private static int maxScanTime=300;
//条形码的最短长度
private static int barcodeMinLength=6;
/**
* 初始键盘代码和字母的对于关系
*/
public BarcodeKeyboardListener(){
keyToLetter.put(48,0);
keyToLetter.put(49,1);
keyToLetter.put(50,2);
keyToLetter.put(51,3);
keyToLetter.put(52,4);
keyToLetter.put(53,5);
keyToLetter.put(54,6);
keyToLetter.put(55,7);
keyToLetter.put(56,8);
keyToLetter.put(57,9);
}
/**
* 此方法响应扫描枪事件
* @param keyCode 
*/
public void onKey(int keyCode) {
//获取输入的是那个数字
Integer letter=keyToLetter.get(keyCode);
if(barcode==null){
//开始进入扫描状态
barcode=new StringBuilder();
//记录开始扫描时间
start=System.currentTimeMillis();
}
//需要判断时间
long cost=System.currentTimeMillis()-start;
if(cost > maxScanTime){
//开始进入扫描状态
barcode=new StringBuilder();
//记录开始扫描时间
start=System.currentTimeMillis();
}
//数字键0-9
if (keyCode >= 48 && keyCode <= 57) {
barcode.append(letter);
}
//回车键
if (keyCode == 13) {
//条形码扫描器在很短的时间内输入了至少 barcodeMinLength 个字符以上信息,并且以“回车”作为结束字符
//进入这个方法表示是“回车”
//那么判断回车之前输入的字符数,至少 barcodeMinLength 个字符
//并且一次扫描要在 maxScanTime 毫秒内完成
if(barcode.length() >= barcodeMinLength && cost < maxScanTime){
cost=System.currentTimeMillis()-start;
System.out.println("耗时:"+cost);
System.out.println(barcode.toString());
//将数据加入缓存阻塞队列
BarcodeBuffer.product(barcode.toString());
}
//清空原来的缓冲区
barcode=new StringBuilder();
}
}
}
package barcode;
/**
*启动和关闭条码枪扫描线程
* @author ysc
*/
public class BarcodeProducter {
private boolean quit;
private Thread thread;
private ScanBarcodeService scanBarcodeService;
public BarcodeProducter(){
scanBarcodeService=new ScanBarcodeService();
}
/**
* 启动生产者线程
* 此方法在tomcat启动的时候被调用
*/
public void startProduct() { 
//防止重复启动
if(thread!=null && thread.isAlive()){
return;
}
System.out.println("启动条形码生产者...");
//启动一个线程用于在tomcat关闭的时候卸载键盘钩子
thread=new Thread() {
@Override
public void run() {
System.out.println("条码枪扫描线程启动");
while (!quit) {
try {
Thread.sleep(Long.MAX_VALUE);
} catch (Exception e) {
quit=true;
}
}
scanBarcodeService.stopScanBarcodeService();
System.out.println("条码枪扫描线程退出");
System.exit(0);
}
};
thread.start();
new Thread() {
@Override
public void run() {
scanBarcodeService.startScanBarcodeService();
}
}.start();
}
/**
* 关闭生产者线程
* 此方法在tomcat关闭的时候被调用
*/
public void stopProduct(){
if(thread!=null){
thread.interrupt();
System.out.println("停止条形码生产者...");
}
}
}
package barcode;
/**
*条形码保存服务
* @author ysc
*/
public interface BarcodeSaveService {
/**
* 保存条形码
* @param barcode 
*/
public void save(String barcode);
/**
* 在这里释放资源,如数据库连接,关闭文件,关闭网络连接等
*/
public void finish();
}
package barcode;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
/**
*把条形码数据保存到文件的实现(参考实现)
* 可根据自己的需求做一个自定义实现
* 只要实现BarcodeSaveService接口的方法
* 并在barcode.save.services文件中指定使用的实现类即可
* @author ysc
*/
public class BarcodeSaveToFile implements BarcodeSaveService{
private Writer writer;
/**
* 保存到文件
* @param barcode 
*/
@Override
public void save(String barcode) {
try {
if(writer==null){
System.out.println("打开文件");
writer=new OutputStreamWriter(new FileOutputStream("d:/barcode.txt",true));
}
writer.write(barcode+"n");
writer.flush();
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* 关闭文件
*/
@Override
public void finish() {
System.out.println("关闭文件");
try {
if(writer!=null){
writer.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
package barcode;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
*扫描服务监听,把此监听器配置到web.xml中
*

barcode.ScanBarcodeListener

* @author ysc
*/
public class ScanBarcodeListener implements ServletContextListener{
private BarcodeProducter barcodeProducter;
private BarcodeConsumer barcodeConsumer;
/**
* tomcat启动
* @param sce 
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
barcodeProducter=new BarcodeProducter();
barcodeProducter.startProduct();
barcodeConsumer=new BarcodeConsumer();
barcodeConsumer.startConsume();
}
/**
* tomcat关闭
* @param sce 
*/
@Override
public void contextDestroyed(ServletContextEvent sce) {
barcodeProducter.stopProduct();
barcodeConsumer.stopConsume();
}
/**
* 可以在此文件中运行测试
* @param args
* @throws Exception 
*/
public static void main(String[] args) throws Exception {
BarcodeProducter producter=new BarcodeProducter();
BarcodeConsumer consumer=new BarcodeConsumer();
producter.startProduct();
consumer.startConsume();
BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
System.out.println("输入 '' 退出程序");
String line=reader.readLine();
while(line!=null){
if("exit".equals(line)){
producter.stopProduct();
consumer.stopConsume();
System.exit(0);
}
line=reader.readLine();
}
}
}
package barcode;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.platform.win32.WinDef.HMODULE;
import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.platform.win32.WinUser.HHOOK;
import com.sun.jna.platform.win32.WinUser.KBDLLHOOKSTRUCT;
import com.sun.jna.platform.win32.WinUser.LowLevelKeyboardProc;
import com.sun.jna.platform.win32.WinUser.MSG;
/**
*
* 利用HOOK技术监听键盘事件(扫描枪就相当于只有0-9和回车键的键盘)
* 低级键盘事件监听
*
*/
public class ScanBarcodeService {
private HHOOK hhkKeyBoard;
private final User32 lib = User32.INSTANCE;
/**
* 停止扫码枪服务
*/
public void stopScanBarcodeService() { 
//卸载键盘钩子
lib.UnhookWindowsHookEx(hhkKeyBoard);
}
/**
* 启动扫码枪服务
*/
public void startScanBarcodeService() {
//键盘事件监听
final BarcodeKeyboardListener listener=new BarcodeKeyboardListener();
//回调
LowLevelKeyboardProc keyboardHook = new LowLevelKeyboardProc() {
@Override
public LRESULT callback(int nCode, WPARAM wParam,
KBDLLHOOKSTRUCT info) {
if (nCode >= 0) {
switch (wParam.intValue()) {
case WinUser.WM_KEYUP:
int keyCode = info.vkCode;
//监听数字键0-9
if (keyCode >= 48 && keyCode <= 57) {
//交个监听器处理
listener.onKey(keyCode);
}
//监听回车键
if (keyCode == 13) {
//交个监听器处理
listener.onKey(keyCode);
}
break;
}
}
//交个下一个钩子
return lib.CallNextHookEx(hhkKeyBoard, nCode, wParam,
info.getPointer());
}
};
HMODULE hMod = Kernel32.INSTANCE.GetModuleHandle(null);
//将上面定义的 回调方法 安装到挂钩链中对系统的底层的键盘输入事件进行监控
hhkKeyBoard = lib.SetWindowsHookEx(WinUser.WH_KEYBOARD_LL, keyboardHook, hMod, 0);
// 处理消息(线程阻塞)
int result;
MSG msg = new MSG();
while ((result = lib.GetMessage(msg, null, 0, 0)) != 0) {
if (result == -1) {
System.err.println("error in get message");
break;
} else {
lib.TranslateMessage(msg);
lib.DispatchMessage(msg);
}
}
}
}